¿Por qué se puede llamar a comunicar () en una tubería componentes distintos de la salida de la última corrompido rendimiento?

Mwuck:

Quiero contar las apariciones de alrededor de 500 patrones en un archivo .fastq grande (59 millones de líneas). Los patrones son todos exactamente 20 caracteres de longitud.

En Unix esto sería un simple:

grep -F -o -f patterns.txt big_file.fastq | sort | uniq -c

Sin embargo, me gustaría evitar escribir un archivo de patrones temporales así que creé una tubería usando la biblioteca de subprocesos de Python:

from subprocess import Popen, PIPE, STDOUT

p1 = Popen(["grep", "-F", "-o", "-f", "-", "big_file.fastq"], shell = False, stdin = PIPE, stdout = PIPE, stderr= STDOUT)
p2 = Popen(["sort"], shell = False, stdin = p1.stdout, stdout = PIPE, stderr = STDOUT)
p3 = Popen(["uniq", "-c"], shell = False, stdin = p2.stdout, stdout = PIPE, stderr = STDOUT)

entonces me comunico llamada () en este, proporcionando un tipo fichero codificado io.StringIO objeto como entrada (que se pasa al comando grep usando el '-'):

import io

patterns_file = io.StringIO("\n".join(patterns_list))
p3.communicate(input = patterns_file.read().encode('utf-8'))[0]

Cuando llamo a comunicar () en uniq así, esto funciona bien.

Sin embargo, mientras que las pruebas que erróneamente llamado en la primera parte de la tubería:

p1.communicate(input = patterns_file.read().encode('utf-8'))[0]

Esto me dio salidas completamente equivocadas, incluyendo partidos que eran más corto o más largo que el espera que 20 caracteres.

No entiendo por qué es. no llamar comunicarse () p1 involucra solamente la parte de la tubería e ignorar el resto? Extracción P2 y P3 P1 causado a grep correctamente. Siento que estoy perdiendo algo acerca de cómo funciona Popen.

Cualquier ayuda es apreciada.

Charles Duffy:

Al crear una instancia Popenobjetos, los subprocesos que se refieren se inician inmediatamente. Por lo tanto, incluso si sólo se llama communicate()en p1, p2y p3también se está ejecutando.

¿Por qué esto es importante? Debido a que p2todavía tiene su entrada estándar adjunto a la FIFO a la que p1está escribiendo su salida!

Si la sortoperación en p2todavía está leyendo el contenido al mismo tiempo que le pida a su programa de Python para leer el mismo contenido directamente, se termina con p1la salida 's repartirse entre ellos. Hilaridad se puede esperar que ensue; sólo si tanto p2y communicate()estaban leyendo bloques que eran múltiplos de 20 bytes serían las divisiones estar sucediendo de forma fiable en los límites de registro, y la porción típica tamaños utilizados para lee sin búfer son múltiplos de 4096.


Por cierto - para muchos programas que esto no tendría un efecto tan grave, porque FIFO tienen una proporción relativamente pequeña de amortiguación; un programa que escribe una línea de salida para cada línea de entrada se lee terminaría bloqueado en la salida muy rápidamente, y por lo tanto sería dejar de leer más de entrada hasta su salida es al menos parcialmente enrojecida. sortes una excepción, ya que tiene que leer todo su entrada antes de que se sabe cuál será su primera línea de salida!

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=398968&siteId=1
Recomendado
Clasificación