Come utilizzare GNU parallel in modo efficace


8

Supponiamo di voler trovare tutte le corrispondenze nel file di testo compresso:

$ gzcat file.txt.gz | pv --rate -i 5 | grep some-pattern

pv --rateutilizzato qui per misurare la portata del tubo. Sulla mia macchina è di circa 420 Mb / s (dopo la decompressione).

Ora sto provando a fare grep in parallelo usando GNU parallel.

$ gzcat documents.json.gz | pv --rate -i 5 | parallel --pipe -j4 --round-robin grep some-pattern

Ora il throughput viene ridotto a ~ 260 Mb / s. E ciò che è il parallelprocesso più interessante stesso sta usando molta CPU. Più dei grepprocessi (ma meno di gzcat).

EDIT 1 : ho provato diverse dimensioni di blocco ( --block), nonché diversi valori per -N/ -Loptions. Niente mi aiuta a questo punto.

Che cosa sto facendo di sbagliato?

Risposte:


9

Sono davvero sorpreso che tu abbia 270 MB / s usando GNU Parallel's --pipe. I miei test di solito raggiungono il massimo a circa 100 MB / s.

Il tuo collo di bottiglia è molto probabilmente in GNU Parallel: --pipenon è molto efficiente. --pipepart, tuttavia, è: qui posso ottenere nell'ordine di 1 GB / s per core della CPU.

Sfortunatamente ci sono alcune limitazioni all'uso --pipepart:

  • Il file deve essere ricercabile (ovvero nessuna pipe)
  • Devi essere in grado di trovare l'inizio di un record con --recstart / - recend (ovvero nessun file compresso)
  • Il numero di riga è sconosciuto (quindi non è possibile avere un record di 4 righe).

Esempio:

parallel --pipepart -a bigfile --block 100M grep somepattern

1
Grazie. C'è qualche motivo per cui --pipeè inefficiente? Voglio dire, è una sorta di problema fondamentale o più di implementazione specifica.
Denis Bazhenov,

2
Sì: GNU Parallel è scritto in perl, e con --pipeogni singolo byte deve passare attraverso il singolo processo, che deve fare un po 'di elaborazione su ogni byte. Con la --pipepartmaggior parte dei byte non vengono mai visualizzati dal processo centrale: vengono elaborati da processi generati. Dato che sono poche le righe che rappresentano il collo di bottiglia --pipe, accolgo con favore un programmatore C / C ++ che riscriverebbe la parte che verrebbe eseguita per le persone che hanno il compilatore C nel loro percorso.
Ole Tange,

2

grep è molto efficace - non ha senso eseguirlo in parallelo. Nel tuo comando solo la decompressione necessita di più CPU, ma questo non può essere parallelo.

La suddivisione dell'input per parallelo richiede più CPU di quanto non sia ottenere linee corrispondenti per grep.

La situazione cambia se si desidera utilizzare invece di grep qualcosa che richiede molta più CPU per ogni linea - quindi il parallelo avrebbe più senso.

Se desideri accelerare questa operazione - guarda dove sono i colli di bottiglia - probabilmente è la decompressione (quindi aiuta a usare altri strumenti di decompressione o una migliore CPU) o - leggi dal disco (quindi aiuta a usare altri strumenti di decompressione o un migliore sistema a disco).

Dalla mia esperienza - a volte è meglio usare lzma (-2 per esempio) per comprimere / decomprimere i file - ha una compressione maggiore di gzip, quindi è necessario leggere meno dati dal disco e la velocità è comparabile.


1
Anzi, è il mio caso. Viene utilizzato un processo Java molto affamato di CPU anziché grep. Ho semplificato un po 'la domanda. Eppure, il consumo parallelo di molta CPU non fornisce molto lavoro ai processi Java.
Denis Bazhenov,

1

La decompressione è il collo di bottiglia qui. Se la decompressione non è parallelizzata internamente, non la raggiungerai da sola. Se hai più di un lavoro del genere, ovviamente lanciali in parallelo, ma la tua pipeline da sola è difficile da parallelizzare. Dividere un flusso in flussi paralleli non è quasi mai valsa la pena e può essere molto doloroso con la sincronizzazione e l'unione. A volte devi solo accettare che più core non ti aiuteranno con ogni singola attività che stai eseguendo.

In generale, la parallelizzazione nella shell dovrebbe essere principalmente a livello di processi indipendenti.


1
Non sembra che la decompressione sia un collo di bottiglia in caso di utilizzo parallel. Concordo sul fatto che è certamente nel primo caso (senza parallelo), ma nel secondo (con parallelo) il collo di bottiglia è sul lato parallelo. Ciò deriva dall'osservazione che il throughput viene ridotto in modo significativo come misurato da pv. Se il collo di bottiglia è in decompressione, la velocità effettiva non cambierà qualsiasi cosa si aggiunga alla pipeline. È una definizione molto intuitiva di throughput, immagino, la cosa che limita maggiormente il throughput.
Denis Bazhenov,

1
È possibile che grep sia così veloce, che finisca più velocemente di quanto parallelpossa scrivere sulla sua pipe. In questo caso, la maggior parte dei grepprocessi semplicemente aspetta di ottenere di più, mentre parallellavora 24 ore su 24 per multiplexare i blocchi in più pipe (che sono operazioni IO aggiuntive e possono persino bloccare la decompressione se il buffer è pieno). Hai provato anche a giocare con il --blockparametro? Il valore predefinito è 1Mfino a quando un grep non ottiene i 1Mdati, il resto è quasi certamente già finito. Pertanto, torniamo al fatto che non ha senso parallelizzare questo.
Orione,

1
Sì, ho provato queste opzioni con blocchi grandi e piccoli. Oltre a valori diversi per -N/ -Lopzioni. Sembra che le opzioni di default siano molto vicine all'ottimale locale che ho riscontrato :)
Denis Bazhenov,

1
Prova a cronometrarlo con e senza pv(con time). In questo modo puoi vedere se pvstesso sta rallentando. In tal caso, parallelcopiare i dati in pipe è sicuramente un sovraccarico aggiuntivo. E in ogni caso, ne sono abbastanza sicuro, grepin questo caso è quasi in tempo reale, specialmente se il pattern è una stringa semplice senza molti backtracking. Inoltre, parallelinterleave e rovinerà gli grepoutput.
Orione,

1
Verificherò che pvquesto non causa il problema, grazie per il consiglio.
Denis Bazhenov,
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.