2008-11-11 14 views
6

a menudo me encuentro escribiendo simple para bucles para llevar a cabo una operación para muchos archivos, por ejemplo:Fácil paralelización

for i in `find . | grep ".xml$"`; do bzip2 $i; done 

Me parece un poco deprimente que en mi máquina de 4 núcleos solo núcleo se está acostumbrando. ¿Existe alguna manera fácil de agregar paralelismo a mis scripts de shell?

EDITAR: Para introducir un poco más de contexto a mis problemas, lo siento, no estaba más claro para empezar!

A menudo quiero ejecutar scripts simples (ish), como trazar un gráfico, comprimir o descomprimir, o ejecutar algún programa, en conjuntos de datos de tamaño razonable (generalmente entre 100 y 10.000). Los scripts que uso para resolver estos problemas se parecen al anterior, pero pueden tener un comando diferente, o incluso una secuencia de comandos para ejecutar.

Por ejemplo, ahora mismo estoy corriendo:

for i in `find . | grep ".xml.bz2$"`; do find_graph -build_graph $i.graph $i; done 

Así que mis problemas son de ninguna manera específica bzip! (Aunque el bzip paralelo se ve bien, tengo la intención de usarlo en el futuro).

+0

Solo una nota, pero puede usar xargs para no tener que escribir un ciclo como ese: buscar. | grep ".xml.bz2 $" | xargs -n1 bzip2 (el -n1 dice que solo pasa cada argumento de bzip 1, por defecto xargs pasa todo a un solo proceso). Lamentablemente, xargs hace cada proceso en serie. –

+0

Deberías hacer 'encontrar. -name \ *. xml.bz2' en lugar de 'encontrar. | grep ".xml.bz2 $" '- ¡eso es precisamente para lo que es encontrar! (Además, su expresión regular coincidirá con nombres de archivos como foozxmlzbz, pero ese es un problema diferente y sin importancia). –

+0

Espera Evan, xargs tiene un argumento marcado "P" para mí, para la cantidad de procesos. Entonces: encontrar. | grep ".xml.bz2 $" | xargs -n1 -P3 bzip2 hace lo que quiero ¿Cuánto tiempo tiene xargs para eso? –

Respuesta

1

Si tuviera que resolver el problema hoy en día es probable que utilice una herramienta como GNU paralelo (a menos que haya una herramienta especializada para parallelized su tarea como pbzip2):

find . | grep ".xml$" | parallel bzip2 

Para obtener más información:

+0

Respuesta actualizada, ¡esto es mejor hoy en día! –

1

Creo que se podría a la siguiente

for i in `find . | grep ".xml$"`; do bzip2 $i&; done 

Pero eso sería escindir sin embargo muchos de los procesos que tiene archivos instantáneamente y no una óptima tan sólo corriendo cuatro procesos a la vez.

+0

Esto estaría bien para trabajos pequeños, pero estaba ejecutando el comando anterior en unos 5.000 archivos. ¡Sospecho que eso mataría a mi computadora muerta de piedra! :) –

+0

Ahoraría otros procesos, pero el programador de Linux es bastante bueno para asegurarse de que los procesos no se mueran completamente. El problema aquí es el uso de memoria, ya que la paginación realmente matará el rendimiento. – sep332

+0

Personalmente me gusta esta respuesta porque funciona sin herramientas adicionales instaladas. Funcionaría bien en una situación en la que está iniciando menos búsquedas de archivos. –

6

This perl program se adapte a sus necesidades bastante bien, usted acaba de hacer esto:

runN -n 4 bzip2 `find . | grep ".xml$"` 
+0

Muchas veces ejecutar más de 4 puede aumentar el rendimiento si tiene 4 procesadores. El quinto y más alto trabajo puede saltar cuando uno de los otros está esperando E/S. – sep332

+0

Buen punto; por otro lado, los cuatro procesos * que compiten * por E/S y líneas de caché a veces pueden ralentizar el proceso total. –

2

La respuesta a la pregunta general es difícil, ya que depende de los detalles de las cosas que está paralelización. Por otro lado, para este propósito específico, debe usar pbzip2 en lugar de bzip2 simple (es probable que pbzip2 ya esté instalado o al menos en los repositorios o su distribución). Vea aquí para detalles: http://compression.ca/pbzip2/

2

Me parece que este tipo de operación es contraproducente. La razón es que cuantos más procesos acceden al disco al mismo tiempo, mayor es el tiempo de lectura/escritura, por lo que el resultado final termina en un tiempo más prolongado. El cuello de botella aquí no será un problema de CPU, no importa cuántos núcleos tenga.

¿Alguna vez ha realizado una simple copia de dos archivos grandes al mismo tiempo en la misma unidad de HD? Por lo general, soy más rápido para copiar uno y luego otro.

Sé que esta tarea implica algo de potencia de la CPU (bzip2 es un método de compresión exigente), pero intente medir la carga de la primera CPU antes de seguir el camino "desafiante" que todos los técnicos tienden a elegir mucho más a menudo de lo necesario.

+0

Usando la secuencia de comandos 'runN' a continuación, si ejecuto 3 copias, obtengo una aceleración 2x ​​(en 4 copias, comienza a disminuir la velocidad otra vez), así que parece que vale la pena hacerlo :) –

+0

Ok, esta vez el camino "desafiante" realmente vale la pena –

+0

Algunos sistemas se ocupan mejor de los accesos de disco concurrentes (¡MUCHO mejor!) que otros. http://stackoverflow.com/questions/9191/how-to-obtain-good-concurrent-read-performance-from-disk – timday

4

gnu make tiene una buena característica de paralelismo (por ejemplo, -j 5) que funcionaría en su caso. Crear un Makefile

%.xml.bz2 : %.xml 


all: $(patsubt %.xml,%xml.bz2,$(shell find . -name '*.xml')) 

luego hacer un

nice make -j 5 

reemplazar '5' con algún número, probablemente, 1 más que el número de CPUs. Es posible que desee hacer 'bueno' esto en caso de que alguien más quiera usar la máquina mientras está en ella.

+0

Iba a sugerir usar make. Pero me la ganaste =) – gnud

+0

@gnud, estoy interesado en cómo escribirías el archivo MAKE (si es diferente de esto). –

2

Hice algo como esto para bash. El truco maquillaje paralelo es probablemente mucho más rápido para las medidas excepcionales, pero aquí es la sección de código principal para implementar algo como esto en bash, tendrá que modificarlo para sus propósitos sin embargo:

#!/bin/bash 

# Replace NNN with the number of loops you want to run through 
# and CMD with the command you want to parallel-ize. 

set -m 

nodes=`grep processor /proc/cpuinfo | wc -l` 
job=($(yes 0 | head -n $nodes | tr '\n' ' ')) 

isin() 
{ 
    local v=$1 

    shift 1 
    while (($# > 0)) 
    do 
    if [ $v = $1 ]; then return 0; fi 
    shift 1 
    done 
    return 1 
} 

dowait() 
{ 
    while true 
    do 
    nj=($(jobs -p)) 
    if ((${#nj[@]} < nodes)) 
    then 
     for ((o=0; o<nodes; o++)) 
     do 
     if ! isin ${job[$o]} ${nj[*]}; then let job[o]=0; fi 
     done 
     return; 
    fi 
    sleep 1 
    done 
} 

let x=0 
while ((x < NNN)) 
do 
    for ((o=0; o<nodes; o++)) 
    do 
    if ((job[o] == 0)); then break; fi 
    done 

    if ((o == nodes)); then 
    dowait; 
    continue; 
    fi 

    CMD & 
    let job[o]=$! 

    let x++ 
done 

wait 
14

Solución : uso xargs para funcionar en paralelo (no se olvide de la opción -n!)

find -name \*.xml -print0 | xargs -0 -n 1 -P 3 bzip2 
+0

Te daré el tic, ya que estoy seguro de que esta es la mejor respuesta :) –

Cuestiones relacionadas