Creazione di un singolo flusso di output su tre altri flussi prodotti in parallelo


10

Ho tre tipi di dati che sono in diversi formati; per ogni tipo di dati, esiste uno script Python che lo trasforma in un unico formato unificato.

Questo script Python è lento e associato alla CPU (a un singolo core su una macchina multi-core), quindi voglio eseguirne tre istanze - una per ciascun tipo di dati - e combinare il loro output per passarlo sort. Fondamentalmente, equivalente a questo:

{ ./handle_1.py; ./handle_2.py; ./handle_3.py } | sort -n

Ma con i tre script eseguiti in parallelo.

Ho trovato questa domanda in cui GNU splitveniva utilizzato per eseguire il round robin di un flusso stdout tra n istanze di uno script che gestisce il flusso.

Dalla pagina man divisa:

-n, --number=CHUNKS
          generate CHUNKS output files.  See below
CHUNKS  may be:
 N       split into N files based on size of input
 K/N     output Kth of N to stdout
 l/N     split into N files without splitting lines
 l/K/N   output Kth of N to stdout without splitting lines
 r/N     like 'l'  but  use  round  robin  distributio

Quindi il r/Ncomando implica " senza dividere le linee ".

Sulla base di questo, sembra che la seguente soluzione dovrebbe essere fattibile:

split -n r/3 -u --filter="./choose_script" << EOF
> 1
> 2
> 3
> EOF

Dove lo choose_scriptfa:

#!/bin/bash
{ read x; ./handle_$x.py; }

Sfortunatamente, vedo un po 'di mescolanza di linee - e molte nuove linee che non dovrebbero esserci.

Ad esempio, se sostituisco i miei script Python con alcuni semplici script bash che fanno questo:

#!/bin/bash
# ./handle_1.sh
while true; echo "1-$RANDOM"; done;

.

#!/bin/bash
# ./handle_2.sh
while true; echo "2-$RANDOM"; done;

.

#!/bin/bash
# ./handle_3.sh
while true; echo "3-$RANDOM"; done;

Vedo questo output:

1-8394

2-11238
2-22757
1-723
2-6669
3-3690
2-892
2-312511-24152
2-9317
3-5981

Questo è fastidioso - basato sull'estratto della pagina man che ho incollato sopra, dovrebbe mantenere l'integrità della linea.

Ovviamente funziona se rimuovo l' -uargomento, ma poi viene bufferizzato e finirò la memoria perché bufferizza l'output di tutti gli script tranne uno.

Se qualcuno ha qualche intuizione qui sarebbe molto apprezzato. Sono fuori di qui.


Alcune persone in #bash su freenode mi hanno suggerito di generare tutti e tre i processi e di metterli in background, scrivere su FD personalizzati, quindi scorrere su questi FD e leggere le righe per loro, ma non ho capito come renderlo fattibile. Mi è stato anche detto di guardare il coprocbuiltin in bash, anche se non vedo davvero come si applica.
Cera,

1
Devi farlo senza file intermedi? Non potresti farlo job1.py > file1 & job2.py > file 2 & job3.py > file3 ; wait ; sort -n file1 file2 file3?
angus,

Risposte:


2

Prova a usare l'opzione -u del GNU parallelo.

echo "1\n2\n3" | parallel -u -IX ./handle_X.sh

Questo li esegue in parallelo, senza bufferizzare l'intero processo.


Sono un po 'confuso - è Xnel IXdire -Iche X sarà la bandiera per la sostituzione, o sta applicando la -Xbandiera, che apparentemente ha anche un significato rilevante?
Cera,

Mah. Lo sto facendo: parallel -u -X ./handle_{}.sh ::: "1" "2" "3"e purtroppo sto ancora vedendo un po 'di manomissione dell'output.
Cera,

il primo: puoi anche usare parallel -u ./handle_{}.sh, ma preferisco cambiarlo, poiché anche le parentesi graffe hanno il significato di unire i comandi (come nella tua domanda).
flowblok,

Sembra funzionare per me, il mio grep non rileva alcun problema : pastie.org/5113187 (stai usando i test script bash o i tuoi script Python attuali?)
flowblok

Il problema è che in realtà non sta facendo nulla in parallelo. Sto usando gli script bash - pastie.org/5113225
Cera il

2

Provare:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py

Se handle_1.pyprende un nome file:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

Non si desidera che l'output sia misto, quindi non usare -u.

Se vuoi mantenere l'ordine (quindi tutto l'output di handle_1 è prima di handle_2 e quindi potresti essere in grado di evitare l'ordinamento):

parallel -k  ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

Se lo desideri ancora ordinato, puoi parallelizzare l'ordinamento e utilizzare sort -m:

parallel --files "./handle_{1}.py {2} | sort -n"  ::: 1 2 3 ::: files* | parallel -j1 -X sort -m

Impostare $ TMPDIR su una directory sufficientemente grande da contenere l'output.


1
Voglio che l'output sia "misto" - Voglio solo assicurarmi che ogni riga nell'output finale sia una singola riga da uno dei sottoprocessi. Se non lo mescolo, il sistema esaurirà la memoria bufferando i flussi stdout che non vengono ancora stampati.
Cera,

Con GNU Parallel non esaurirai la memoria: non bufferizza in memoria. Perché pensi che buffer nella memoria?
Ole Tange,

2

Forse mi manca qualcosa, ma non puoi semplicemente fare:

(./handle_1.py & ./handle_2.py & ./handle_3.py) | sort -n

Se si desidera che le righe di ciascun processo non vengano intercalate, è più semplice assicurarsi che il processo stesso le scriva completamente e che possibilmente disabiliti il ​​buffering di output poiché le writes su una pipe sono garantite come atomiche purché non siano più grandi di PIPE_BUF. Per esempio, si potrebbe fare in modo che fa il buffering uso uscita à la stdioe la chiamata fflusho qualunque sia l'equivalente è in pythondopo una o poche righe sono state scritte.

Se non riesci a modificare gli script Python, puoi fare:

lb() { grep --line-buffered '^'; }

(con GNU grep) o:

lb() while IFS= read -r l; do printf '%s\n' "$l"; done

(Vedi le note nei commenti qui sotto se ciò che l'output dei comandi non è di testo)

E fai:

(./handle_1.py | lb & ./handle_2.py | lb & ./handle_3.py | lb) | sort -n

Un'altra opzione per evitare questi 3 lbprocessi è quella di avere tre pipe per un comando che usa select/ pollper vedere da dove proviene sortun po 'di output e alimentarlo in linea, ma ci vuole un po' di programmazione.


Hai bisogno di un waitdentro, credo.
derobert,

1
No, a meno che alcuni dei programmi non chiudano il loro stdout prima di uscire, perché il pipe e sort -nrimarrà fino a quando tutti i programmi che hanno un fd aperto su di esso sono usciti.
Stéphane Chazelas,

Anzi, ho provato, hai ragione.
derobert,

No, ho ancora un output distorto. Le linee si mescolano e si alternano.
Cera,

1
OK @Cerales, vedi la mia risposta aggiornata
Stéphane Chazelas il

1

La risposta di Flowbok era la soluzione corretta. Stranamente, l'output di GNU parallelviene alterato se viene emesso direttamente in un file, ma non se va in tty.

Fortunatamente, script -cè disponibile per imitare un tty.

Ci sono ancora i tre script:

#!/bin/bash
# handle_1.sh
while true; do echo "1-$RANDOM$RANDOM$RANDOM$RANDOM"; done

.

#!/bin/bash
# handle_2.sh
while true; do echo "2-$RANDOM$RANDOM$RANDOM$RANDOM"; done

.

#!/bin/bash
# handle_3.sh
while true; do echo "3-$RANDOM$RANDOM$RANDOM$RANDOM"; done

Quindi c'è un file che incapsula la chiamata in parallelo:

#!/bin/bash
# run_parallel.sh
parallel -u -I N ./handle_N.sh ::: "1" "2" "3"

E poi lo chiamo così:

script -c ./run_parallel > output

Le righe nell'output vengono mescolate riga per riga tra l'output dei diversi script, ma non vengono alterate o interfogliate su una determinata riga.

Comportamento bizzarro da parallel- Potrei presentare una segnalazione di bug.

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.