Tengo una tarea que procesa una lista de archivos en stdin. El tiempo de inicio del programa es considerable, y la cantidad de tiempo que toma cada archivo varía ampliamente. Quiero generar una cantidad sustancial de estos procesos, luego enviar el trabajo a los que no estén ocupados. Hay varias herramientas de línea de comandos diferentes que casi hacen lo que quiero, lo reduje a dos opciones casi funcionales:
find . -type f | split -n r/24 -u --filter="myjob"
find . -type f | parallel --pipe -u -l 1 myjob
El problema es que split
hace un round robin puro, por lo que uno de los procesos se queda atrás y queda atrás, retrasando la finalización de toda la operación; mientras que parallel
desea generar un proceso por N líneas o bytes de entrada y termino gastando demasiado tiempo en la sobrecarga de inicio.
¿Hay algo como esto que reutilizará los procesos y las líneas de alimentación a los procesos que hayan desbloqueado stdins?
fuente
split
comando? El nombre entra en conflicto con la utilidad de procesamiento de texto estándar .myjob
está lista para recibir más información. No hay forma de saber que un programa está listo para procesar más entradas, todo lo que puede saber es que algún búfer en algún lugar (un búfer de tubería, un búfer estándar) está listo para recibir más entrada. ¿Puede organizar que su programa envíe algún tipo de solicitud (por ejemplo, mostrar un mensaje) cuando esté listo?read
llamadas funcionaría. Ese es un esfuerzo de programación bastante grande.-l 1
en losparallel
argumentos? IIRC, que le dice a paralelo que procese una línea de entrada por trabajo (es decir, un nombre de archivo por bifurcación de myjob, por lo que hay mucha sobrecarga de inicio).Respuestas:
Eso no parece posible en un caso tan general. Implica que tiene un búfer para cada proceso y puede ver los búferes desde el exterior para decidir dónde colocar la siguiente entrada (programación) ... Por supuesto, puede escribir algo (o usar un sistema por lotes como slurm)
Pero dependiendo de cuál sea el proceso, es posible que pueda preprocesar la entrada. Por ejemplo, si desea descargar archivos, actualizar entradas de una base de datos o similar, pero el 50% de ellas terminarán siendo omitidas (y por lo tanto tiene una gran diferencia de procesamiento dependiendo de la entrada), simplemente configure un preprocesador que verifica qué entradas van a tomar mucho tiempo (existe un archivo, se cambiaron los datos, etc.), por lo que se garantiza que todo lo que provenga del otro lado llevará una cantidad de tiempo bastante igual. Incluso si la heurística no es perfecta, puede terminar con una mejora considerable. Puede volcar los demás en un archivo y procesarlos de la misma manera.
Pero eso depende de su caso de uso.
fuente
No, no hay una solución genérica. Su despachador necesita saber cuándo cada programa está listo para leer otra línea, y no hay un estándar que yo sepa que lo permita. Todo lo que puede hacer es poner una línea en STDOUT y esperar a que algo lo consuma; No hay realmente una buena manera para que el productor en una tubería sepa si el próximo consumidor está listo o no.
fuente
No lo creo. En mi revista favorita había un artículo sobre programación de bash que hacía lo que querías. Estoy dispuesto a creer que si hubiera herramientas para hacerlo, las habrían mencionado. Entonces quieres algo en la línea de:
Obviamente, puede cambiar la invocación al script de trabajo real a su gusto. La revista que mencioné inicialmente hace cosas como instalar tuberías y, en realidad, iniciar hilos de trabajo. Esté atento
mkfifo
a eso, pero esa ruta es mucho más complicada ya que los procesos de los trabajadores necesitan indicarle al proceso maestro que están listos para recibir más datos. Por lo tanto, necesita un quince para cada proceso de trabajo para enviar datos y un quince para que el proceso maestro reciba cosas de los trabajadores.DESCARGO DE RESPONSABILIDAD Escribí ese guión desde la parte superior de mi cabeza. Puede tener algunos problemas de sintaxis.
fuente
find . -type f | while read i
lugar defor i in $(find . -type f)
.Para GNU Parallel puede establecer el tamaño del bloque usando --block. Sin embargo, requiere que tenga suficiente memoria para mantener 1 bloque en la memoria para cada uno de los procesos en ejecución.
Entiendo que esto no es precisamente lo que está buscando, pero puede ser una solución aceptable por ahora.
Si sus tareas en promedio toman el mismo tiempo, entonces podría usar mbuffer:
fuente
Prueba esto:
mkfifo
para cada procesoLuego
tail -f | myjob
espera cada quince.Por ejemplo, la configuración de los trabajadores (procesos myjob)
Dependiendo de su aplicación (myjob), es posible que pueda usar jobs -s para encontrar trabajos detenidos. De lo contrario, enumere los procesos ordenados por CPU y seleccione el que consuma la menor cantidad de recursos. De tener el informe de trabajo en sí mismo, por ejemplo, estableciendo una bandera en el sistema de archivos cuando quiere más trabajo.
Suponiendo que el trabajo se detiene al esperar la entrada, use
jobs -sl
para averiguar el pedido de un trabajo detenido y asignarle trabajo, por ejemploProbé esto con
Debo admitir que esto fue inventado, así que mmm.
fuente
Lo que realmente se necesita para resolver esto es un mecanismo de cola de algún tipo.
¿Es posible que los trabajos lean su entrada de una Cola, como una cola de mensajes SYSV, y luego que los programas se ejecuten en paralelo simplemente empujando los valores a la cola?
Otra posibilidad es usar un directorio para la cola, así:
pending
mv
del primer archivo que ve en el directorio a un directorio hermano depending
, nombradoinprogress
.pending
fuente
exponiendo la respuesta de @ ash, puede usar una cola de mensajes SYSV para distribuir el trabajo. Si no desea escribir su propio programa en C, hay una utilidad llamada
ipcmd
que puede ayudar. Esto es lo que he puesto juntos para aprobar la salidafind $DIRECTORY -type f
de$PARALLEL
varios procesos:Aquí hay una prueba de funcionamiento:
fuente
A menos que pueda estimar cuánto tiempo se procesará un archivo de entrada particular y los procesos de trabajo no tienen una forma de informar al planificador (como lo hacen en escenarios de computación paralela normales, a menudo a través de MPI ), generalmente no tiene suerte - pagar la penalidad de algunos trabajadores que procesan información por más tiempo que otros (debido a la desigualdad de información), o pagar la penalidad de generar un único proceso nuevo para cada archivo de entrada.
fuente
GNU Parallel ha cambiado en los últimos 7 años. Entonces hoy puede hacerlo:
Este ejemplo muestra que se dan más bloques al proceso 11 y 10 que al proceso 4 y 5 porque 4 y 5 leen más lentamente:
fuente