我有一个文本文件infile.txt
:
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?
文件中的每一行都将通过此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
但是,有一种不那么冗长的方法吗?
答案 0 :(得分:15)
@Ulfalizer的答案为您提供了解决方案的良好提示,但缺乏细节。
您可以使用GNU parallel(Debian上的apt-get install parallel
)
因此,您可以使用以下命令解决问题:
parallel -a infile.txt -l 1000 -j10 -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
答案 1 :(得分:6)
我自己从未尝试过,但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
这是否值得,取决于您的处理CPU密集程度。对于简单的脚本,您将花费大部分时间将数据移入和移出磁盘,并行化并不会让您获得更多。
您可以通过GNU Parallel作者here找到一些介绍性视频。
答案 2 :(得分:4)
假设您的限制因素不是您的磁盘,您可以使用fork()
并特别Parallel::ForkManager
在perl中执行此操作:
#!/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,那么并行性无论如何都无济于事)。
虽然有各种可能的解决方案 - 但是在perl文档中写了一整章:perlipc - 但请记住,您也可以使用Parallel::ForkManager
检索数据。