unix 如何拆分文件并并行处理它们,然后将它们缝合回去?

6rqinv9w  于 2023-01-13  发布在  Unix
关注(0)|答案(3)|浏览(164)

我有一个文本文件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

但是,有没有一种不那么冗长的方法来做同样的事情呢?

azpvetkf

azpvetkf1#

@Ulfalizer的回答给了你一个关于解决方案的很好的提示,但缺少一些细节。
您可以使用GNU parallel(Debian上的apt-get install parallel
因此,您的问题可以使用以下命令解决:

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

以下是这些参数的含义:

-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
t9aqgxwy

t9aqgxwy2#

我自己从来没有试过,但是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的一些介绍性视频。

bxgwgixi

bxgwgixi3#

假设您的限制因素不是磁盘,您可以在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,那么并行性无论如何都不会有多大帮助)。
有各种可能的解决方案,虽然-这么多,写了整整一章,它在perl文档:perlipc-但请记住,您也可以使用Parallel::ForkManager检索数据。

相关问题