Anelli a guscio parallelo


11

Voglio elaborare molti file e poiché ho qui un mucchio di core, voglio farlo in parallelo:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

Conosco una soluzione Makefile ma i miei comandi hanno bisogno degli argomenti fuori dalla lista dei globbing della shell. Quello che ho trovato è:

> function pwait() {
>     while [ $(jobs -p | wc -l) -ge $1 ]; do
>         sleep 1
>     done
> }
>

Per usarlo, tutto ciò che si deve fare è mettere e dopo i lavori e una chiamata pwait, il parametro fornisce il numero di processi paralleli:

> for i in *; do
>     do_something $i &
>     pwait 10
> done

Ma questo non funziona molto bene, ad esempio l'ho provato con ad esempio un ciclo for che converte molti file ma mi dà errore e lascia i lavori annullati.

Non riesco a credere che non sia stato ancora fatto poiché la discussione sulla mailing list di zsh è ormai obsoleta. Quindi conosci qualcosa di meglio?


Simile a questa domanda: superuser.com/questions/153630/… Vedi se quella tecnica funziona per te.
JRobert,

Sarebbe utile se hai pubblicato i messaggi di errore.
In pausa fino a ulteriore avviso.

@JRobert sì, lo sapevo, ma questo non aiuta in realtà perché l'approccio makefile non funzionerà come ho detto! @Dennis: Ok, prima ho lasciato correre un top accanto a mostrarmi più del numero specificato di processi. Secondo, non ritorna correttamente al prompt. Terzo che ho detto che lascia i lavori annullati non era giusto: ho appena posizionato un indicatore echo "DONE"dopo il ciclo che è stato eseguito prima che i lavori attivi non fossero finiti. => Questo mi ha fatto pensare che i lavori non fossero stati fatti.
matematica

Risposte:


15

Un makefile è una buona soluzione al tuo problema. Potresti programmare questa esecuzione parallela in una shell, ma è difficile, come hai notato. Un'implementazione parallela di make non si occuperà solo dell'avvio dei lavori e del loro rilevamento, ma gestirà anche il bilanciamento del carico, il che è difficile.

Il requisito per il globbing non è un ostacolo: ci sono implementazioni che lo supportano. GNU make, che ha espansione con caratteri jolly come $(wildcard *.c)e accesso alla shell come $(shell mycommand)(cercare le funzioni nella GNU make manual per ulteriori informazioni). È l'impostazione predefinita makesu Linux e disponibile sulla maggior parte degli altri sistemi. Ecco uno scheletro di Makefile che potresti essere in grado di adattare alle tue esigenze:

fonti = $ (carattere jolly * .src)

tutto: $ (fonti: .src = .tgt)

% .tgt: $ .src
    do_something $ <$$ (deriv_params $ <)> $ @

Eseguire qualcosa come make -j4eseguire quattro lavori in parallelo o make -j -l3mantenere la media del carico intorno a 3.


8

Non sono sicuro di come siano i tuoi argomenti derivati. Ma con GNU Parallel http: // www.gnu.org/software/parallel/ puoi farlo per eseguire un lavoro per core cpu:

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

Se ciò che vuoi derivare è semplicemente cambiare l'estensione. {.} Può essere utile:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

Guarda il video introduttivo su GNU Parallel su http://www.youtube.com/watch?v=OpaiGYxkSuQ


7

L'uso del waitcomando della shell non funzionerebbe per te?

for i in *
do
    do_something $i &
done
wait

Il ciclo esegue un lavoro, quindi lo attende, quindi esegue il lavoro successivo. Se quanto sopra non funziona per te, allora il tuo potrebbe funzionare meglio se ti muovi pwaitdopo done.


no con 1 milione di file avrei 1 milione di processi in esecuzione, o sbaglio?
matematica,

1
@brubelsabs: Beh, sarebbe cercare di fare un milione di processi. Non hai detto nella tua domanda quanti file hai bisogno di elaborare. Penserei che dovresti usare i forloop nidificati per limitare questo: for file in *; do for i in {1..10}; do do_something "$i" & done; wait; done(non testato) Questo dovrebbe fare dieci alla volta e attendere fino a quando tutti e dieci i gruppi sono terminati prima di iniziare i dieci successivi. Il tuo ciclo fa uno alla volta facendo il &moot. Vedi la domanda a cui JRobert ha collegato per altre opzioni. Cerca su Stack Overflow altre domande simili alle tue (e quella).
In pausa fino a ulteriore avviso.

Se l'OP anticipa un milione di file, avrebbe un problema for i in *. Avrebbe dovuto passare argomenti al loop con una pipe o qualcosa del genere. Quindi invece di un ciclo interno potresti eseguire un contatore incrementale ed eseguire "micro-"wait"-s"ogni "$ ((i% 32))" -eq '0'

@DennisWilliamson: la combinazione waitcon un contro-loop interno ha funzionato bene per me. Grazie!
Joel Purra,

3

Perché nessuno ha ancora menzionato xargs?

Supponendo che tu abbia esattamente tre argomenti,

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

Altrimenti usa un delimitatore (null è utile per quello):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

EDIT: per quanto sopra, ogni parametro dovrebbe essere separato da un carattere null, quindi il numero di parametri dovrebbe essere specificato con xargs -n.


Sì, nel nostro progetto qualcuno ha avuto la stessa idea e funziona benissimo anche con Windows con MSys.
matematica

0

Ho provato alcune delle risposte. Rendono lo script un po 'più complesso di quanto sia necessario. L'utilizzo ideale parallelo xargssarebbe preferibile, tuttavia, se le operazioni all'interno del ciclo for sono complicate, potrebbe essere problematico creare file di grandi e lunghe linee da fornire in parallelo. invece potremmo usare source come segue

# Create a test file 
$ cat test.txt
task_test 1
task_test 2

# Create a shell source file 
$ cat task.sh
task_test()
{
    echo $1
}

# use the source under bash -c 
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2

Quindi per il tuo problema la soluzione sarebbe simile

for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done

definire fare qualcosa come do_something.sh

do_something(){
process $1
echo $2 
whatever $3 

}

eseguire con xargognu parallel

   cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'

Presumo che sia implicita l'indipendenza funzionale delle iterazioni di for.

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.