Ren*_*aud 2 parallel-processing perl while-loop
我有一个 Perl 脚本,我希望并行化。
while它由一个超过 11000 行的循环和另一个while3400 行的循环组成,这使得它非常慢。
open (FILE1, "File1.txt") or die "Can't open File1";
open (OUT, ">Outfile.txt");
while (<FILE1>)
{
my @data=split (/ /, $_);
my $RS=1;
open (FILE2, "File2.txt") or die "Can't open File2";
while (<FILE2>)
{
my @value=split (/ /, $_);
if ($data[$RS] == 1) {print OUT $value[1];$RS++;}
elsif ($data[$RS] == 2) {print OUT $value[2];$RS++;}
elsif ($data[$RS] == 0) {print OUT $value[3];$RS++;}
}
close FILE2;
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找一种方法来对每一行执行相当于 qsub 的操作,File1这样我就可以发送 3440 个作业。有什么建议么?如果可能的话我想继续使用 perl。我尝试将此代码插入到 bash 脚本中,但我不太明白如何将一种语言插入另一种语言中。
我的File1包含 ID 列表,列中包含信息。然后,每一列都与 中的一行相关File2。我希望能够同时为多个 ID 运行第二个循环,而不是一个接一个地运行。
File1
ID RS_10 RS_15 RS_30
23 1 0 1
34 2 2 0
45 1 1 0
23 0 0 2
10 2 1 1
File2
RS_10 A B C
RS_15 D E F
RS_30 G H I
Run Code Online (Sandbox Code Playgroud)
优化的第一条规则是不要太早(即在不分析代码的情况下就得出过早的结论)。
\n\n第二条规则可能涉及缓存。
\n\n你的File2不是很大。我想说我们将它加载到内存中。这样做有以下优点:
关于第一点:您将每行分割了三千多次。这些周期本来可以更好地度过。
\n\n关于第三点:您似乎进行了索引转换:
\n\n1 \xe2\x86\x92 1, 2 \xe2\x86\x92 2, 0 \xe2\x86\x92 3\nRun Code Online (Sandbox Code Playgroud)\n\n我们可以使用一个数组来执行此转换(恒定时间查找),而不是使用 if/elsif 开关(线性复杂度)测试所有值:
\n\nmy @conversion = (3, 1, 2);\n...;\nprint OUT $value[$conversion[$data[$RS++]]];\nRun Code Online (Sandbox Code Playgroud)\n\n如果这个索引转换是恒定的,我们在解析时可以做一次且只能做一次File2。这看起来像
use strict; use warnings;\nuse autodie; # automatic error handling\n\nmy @file2;\n{\n open my $file2, "<", "File2.txt";\n while (<$file2>) {\n my (undef, @vals) = split;\n\n # do the reordering. This is equivalent to @vals = @vals[2, 0, 1];\n unshift @vals, pop @vals;\n\n push @file2, \\@vals;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们可以继续迭代File1. 从现在开始打印相应的条目File2看起来像
open my $file1, "<", "File1.txt";\n<$file1>; # remove header\nwhile (<$file1>) {\n my ($id, @indices) = split;\n print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;\n # but I guess you\'d want some separator in between\n # If so, set the $, variable\n}\nRun Code Online (Sandbox Code Playgroud)\n\n该算法仍然是二次的(map只是for变相的循环),但这应该有一个更好的常数因子。给定示例输入的上述代码的输出是
23 A F G\n34 B E I\n45 A D I\n23 C F H\n10 B D G\nRun Code Online (Sandbox Code Playgroud)\n\n(和$, = " "; $\\ = "\\n")。
最后一步(循环通过 File1)可以并行化,但这不太可能有多大帮助:IO 很慢,线程之间的通信成本很高(IPC 更是如此),并且输出将按随机顺序排列。我们可以生成一群工作人员,并在队列中传递未解析的行:
\n\nuse threads; # should be 1st module to be loaded\nuse Thread::Queue;\nuse constant NUM_THREADS => 4; # number of cores\n\n# parse the File2 data here\n\nmy $queue = Thread::Queue->new;\nmy @threads = map threads->new(\\&worker), 1 .. NUM_THREADS;\n\n# enqueue data\n$queue->enqueue($_) while <$file1>;\n# end the queue\n$queue->enqueue((undef) x NUM_THREADS); # $queue->end in never versions\n\n# wait for threads to complete\n$_->join for @threads;\n\nsub worker {\n while(defined(my $_ = $queue->dequeue)) {\n my ($id, @indices) = split;\n print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,这会将 复制@file2到所有线程中。有趣的事实:对于示例数据,该线程解决方案大约需要 4\xc3\x97 的时间。这主要是线程创建的开销,因此这对于您的数据来说不是什么问题。
无论如何,分析您的代码以查看可以最有效地优化的地方。我推荐优秀的Devel::NYTProf. 例如,对于我使用非常有限的数据运行的非线程测试,autodie朋友们所隐含的开销比实际处理花费的时间更多。对您来说,最贵的线路可能是
print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;\nRun Code Online (Sandbox Code Playgroud)\n\n但在 Perl 中我们无能为力。
\n