Esecuzione di comandi in parallelo con un limite di numero simultaneo di comandi


23

Sequenziale: for i in {1..1000}; do do_something $i; done- troppo lento

Parallelo: for i in {1..1000}; do do_something $i& done- carico eccessivo

Come eseguire comandi in parallelo, ma non più di, ad esempio, 20 istanze al momento?

Ora di solito usando hack come for i in {1..1000}; do do_something $i& sleep 5; done, ma questa non è una buona soluzione.

Aggiornamento 2 : conversione della risposta accettata in uno script: http://vi-server.org/vi/parallel

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Si noti che è necessario sostituire 8 spazi con 2 schede prima di "i =" per farlo funzionare.

Risposte:


15

GNU Parallel è fatto per questo.

seq 1 1000 | parallel -j20 do_something

Può anche eseguire lavori su computer remoti. Ecco un esempio per ricodificare un MP3 in OGG usando server2 e computer locale che esegue 1 lavoro per core della CPU:

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

Guarda un video introduttivo su GNU Parallel qui:

http://www.youtube.com/watch?v=OpaiGYxkSuQ


Non sono a conoscenza di "moreutils" e che esiste già uno strumento per il lavoro. Guardare e confrontare.
Vi.

1
In parallelmoreutils non è GNU Parallel ed è abbastanza limitato nelle sue opzioni. Il comando sopra non verrà eseguito con il parallelo di moreutils.
Ole Tange,

1
Una possibilità più: xargs --max-procs=20.
Vi.

4

Non una soluzione bash, ma dovresti usare un Makefile, possibilmente con -lper non superare un carico massimo.

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

Quindi avviare 20 lavori alla volta

$ make -j20

o per avviare quanti più lavori possibile senza superare un carico di 5

$ make -j -l5

Sembra la soluzione non hacky per ora.
Vi.

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20Ora sembra di nuovo più confuso.
Vi.

@vi: oh mio ....
Benjamin Bannier,

Converti la tua soluzione in uno script. Ora può essere utilizzato con facilità.
Vi.

2

pubblicare lo script nella domanda con la formattazione:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Si noti che è necessario sostituire 8 spazi con 2 schede prima di "i =".


1

Un'idea semplice:

Controlla i modulo 20 ed esegui il comando wait shell prima di do_something.


Attenderà il completamento di tutte le attività correnti (creazione di rallentamenti nel numero di grafici delle attività) o attenderà un'attività specifica che può bloccarsi per un periodo più lungo (creando di nuovo cedimenti in questo caso)
Vi.

@Vi: l'attesa della shell è per tutte le attività in background che appartengono a questa shell.
harrymc,

1

È possibile utilizzare psper contare il numero di processi in esecuzione e ogni volta che scende al di sotto di una determinata soglia, si avvia un altro processo.

Pseudo codice:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

Può essere while [ `jobs | wc -l` -ge 20]; do?
Vi.

certo, ma nel mio esempio, dovrei calcolare njobsdue volte, e le prestazioni sono abbastanza importanti negli script di shell che eseguono attività di
sospensione

Voglio dire che la tua versione non funziona come previsto. Passo sleep 1a sleep 0.1e comincio a dare un punteggio medio a 40-50 invece di 20. Se ci sono più di 20 lavori, dobbiamo aspettare che il lavoro sia finito, non aspettare solo 1 secondo.
Vi.

0

puoi farlo in questo modo.

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

usando named pipe, ogni volta esegue 20 sub shell in parallelo.

Spero che sia d'aiuto :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.