2015-03-13 20 views
13

Ho un file di testo infile.txt come tale:Come dividere i file e elaborarli in parallelo e poi ricucirli? unix

abc what's the foo bar. 
foobar hello world, hhaha cluster spatio something something. 
xyz trying to do this in parallel 
kmeans you're mean, who's mean? 

Ogni riga del file saranno trattati da questo comando perl nella out.txt

`cat infile.txt | perl dosomething > out.txt` 

Immaginate se il file di testo è 100.000.000 linee . Voglio parallelizzare il comando bash così ho provato qualcosa di simile:

$ mkdir splitfiles 
$ mkdir splitfiles_processed 
$ cd splitfiles 
$ split -n3 ../infile.txt 
$ for i in $(ls); do "cat $i | perl dosomething > ../splitfiles_processed/$i &"; done 
$ wait 
$ cd ../splitfiles_processed 
$ cat * > ../infile_processed.txt 

Ma c'è un modo meno prolissa a fare lo stesso?

+2

Perché dividerlo Comunque? Inoltre c'è un abuso di 'gatto' in là. 'Perl' può fare le modifiche sul posto, può anche fare una copia di backup. –

+1

Sembra un problema XY.Che problema stai cercando di risolvere che ti porta a voler usare questo tipo di soluzione? –

+0

Tratta il 'gatto ... | ... 'come' # fai qualcosa '. Ho bisogno di dividere un file per elaborarlo in parti e poi ricucire le parti processate in un singolo file. Non ho alcun controllo su "#do qualcosa", quindi lo alimento con un file di testo e restituisce un file di testo elaborato. – alvas

risposta

11

La risposta di @Ulfalizer offre un buon suggerimento sulla soluzione, ma manca di dettagli.

È possibile utilizzare GNU parallel (apt-get install parallel su Debian)

Quindi il problema può essere risolto utilizzando il seguente comando:

parallel -a infile.txt -l 1000 -j10 -k --spreadstdin perl dosomething > result.txt 

Qui è il significato di argomenti

-a: read input from file instead of stdin 
-l 1000: send 1000 lines blocks to command 
-j 10: launch 10 jobs in parallel 
-k: keep sequence of output 
--spreadstdin: sends the above 1000 line block to the stdin of the command 
+0

Grazie Adam! Per diffondere i punti, se va bene, il segno di spunta va a te e @Ulfalizer ottiene la taglia =) – alvas

+0

Awesome Parallel –

5

Non l'ho mai provato da solo, ma vale la pena verificare GNU parallel.

Ecco un estratto dalla pagina man (parallel(1)) che è simile a quello che stai facendo attualmente. Può dividere l'input anche in altri modi.

 
EXAMPLE: Processing a big file using more cores 
     To process a big file or some output you can use --pipe to split up 
     the data into blocks and pipe the blocks into the processing program. 

     If the program is gzip -9 you can do: 

     cat bigfile | parallel --pipe --recend '' -k gzip -9 >bigfile.gz 

     This will split bigfile into blocks of 1 MB and pass that to gzip -9 
     in parallel. One gzip will be run per CPU core. The output of gzip -9 
     will be kept in order and saved to bigfile.gz 

Se ciò vale la pena dipende da quanto è intensiva la CPU. Per gli script semplici, trascorrerai la maggior parte del tempo a mischiare dati da e verso il disco, e parallelizzare non ti darà molto.

È possibile trovare alcuni video introduttivi dell'autore GNU Parallelo here.

4

Assumendo che il fattore limitante non è il vostro disco, è possibile farlo in perl con fork() e specificatamente Parallel::ForkManager:

#!/usr/bin/perl 

use strict; 
use warnings; 

use Parallel::ForkManager; 

my $max_forks = 8; #2x procs is usually optimal 

sub process_line { 
    #do something with this line 
} 

my $fork_manager = Parallel::ForkManager -> new ($max_forks); 

open (my $input, '<', 'infile.txt') or die $!; 
while (my $line = <$input>) { 
    $fork_manager -> start and next; 
    process_line ($line); 
    $fork_manager -> finish; 
} 

close ($input); 
$fork_manager -> wait_all_children(); 

L'aspetto negativo di fare qualcosa di simile a questo, però è quella di coalescenza vostra uscita. Ogni attività parallela non necessariamente termina nella sequenza in cui è stata avviata, quindi si hanno tutti i tipi di potenziali problemi relativi alla serializzazione dei risultati.

È possibile aggirare questi con qualcosa come flock ma è necessario fare attenzione, come troppe operazioni di blocco può togliere il vantaggio parallelo in primo luogo. (Quindi la mia prima affermazione - se il tuo fattore limitante è l'IO del disco, allora il parallelismo comunque non aiuta molto).

Tuttavia, ci sono varie soluzioni possibili - tanto ne ha scritto un intero capitolo nei documenti perl: perlipc - ma tieni presente che puoi recuperare i dati anche con Parallel::ForkManager.

+0

Mi piace Parallel :: ForkManager, ma non elaborare una riga alla volta. Elaborazione di un frammento del file - sì, una linea - no, troppo fork overhead. –

+0

Sta replicando la richiesta dell'OP. Ma sì, piuttosto dipenderebbe da quanto "sforzo" è necessario per ogni linea. 'fork' è abbastanza leggero - ed è ciò che accadrà per es. qualsiasi tipo di operazione in stile parallelo di GNU. – Sobrique

+0

La forcella non è leggera. È tra i più pesanti. –