Подтвердить что ты не робот

Как разбить файлы и обработать их параллельно, а затем сшить их? Юникс

У меня есть текстовый файл infile.txt как таковой:

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

Каждая строка в файле будет обрабатываться этой командой perl в out.txt

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

Представьте, если текстовый файл содержит 100 000 000 строк. Я хочу распараллелить команду bash, поэтому я попробовал что-то вроде этого:

$ 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

Но существует ли менее верный способ сделать то же самое?

4b9b3361

Ответ 1

Ответ @Ulfalizer дает вам хороший совет о решении, но в нем отсутствуют некоторые детали.

Вы можете использовать GNU параллельно (apt-get install parallel в Debian)

Таким образом, ваша проблема может быть решена с помощью следующей команды:

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

Вот смысл аргументов:

-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

Ответ 2

Я никогда не пробовал это самостоятельно, но GNU parallel, возможно, стоит проверить.

Здесь выдержка из справочной страницы (parallel(1)), аналогичная тому, что вы сейчас делаете. Он также может разделить вход другими способами.

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

Независимо от того, стоит ли это, зависит от того, насколько интенсивна загрузка процессора. Для простых скриптов вы будете тратить большую часть времени на перетасовку данных на диск и с него, а распараллеливание не принесет вам многого.

Вы можете найти несколько вводных видео от автора параллельной работы GNU здесь.

Ответ 3

Предполагая, что ваш ограничивающий фактор НЕ является вашим диском, вы можете сделать это в perl с помощью fork() и, в частности, 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();

Недостатком делать что-то вроде этого является то, что объединить ваш выход. Каждая параллельная задача не обязательно заканчивается в начавшейся последовательности, поэтому у вас есть всевозможные потенциальные проблемы, связанные с сериализацией результатов.

Вы можете обойти их с чем-то вроде flock, но вам нужно быть осторожным, так как слишком много операций блокировки могут убрать ваше параллельное преимущество в первую очередь. (Отсюда мой первый оператор - если ваш лимитирующий фактор - диск IO, то parallelism вообще не помогает вообще).

Там могут быть различные возможные решения - так написано целая глава в нем в perl docs: perlipc - но имейте в виду вы можете получать данные с помощью Parallel::ForkManager.