ForkManager在调试模式下更快完成

时间:2017-01-24 08:04:11

标签: multithreading performance perl

我正在试图弄清楚如何正确地为我正在进行的项目的一部分实现ForkManager,但是遇到了FM似乎正在产生进程和做事情的情况,但需要永远。

但是,当我在调试代码中尝试FM时(通过将max processes设置为0),代码在合理的预期时间范围内完成。

以下是我遇到问题的代码......

<div class="form-group">
         <label for="annualeave" class="col-sm-8">Annual Leave :</label>
         <div class="col-sm-4">
         <input type="text" class="form-control" id="annualeave" name="annualeave" ng-model="annualeave" placeholder="Enter the details" required>
        </div>
</div>

因此,如果我将use strict; use warnings; use Parallel::ForkManager; sub read_table { # takes a filename and reads in a CSV file. # works fine and thus is omitted here } sub foo { # originally an Inline::C subroutine # for purpose of debugging replaced with randgen return rand; } my $cpu_count = 0; my $epsilon = 1e-16; my @tt = read_table('tt.csv'); my @tc = read_table('tc.csv'); my @nm = ($epsilon) x scalar @tc; my @results; my $pm = new Parallel::ForkManager($cpu_count); $pm->run_on_finish(sub{ my $i = $_[1]; my $m = $_[5]; $results[$i] = $m; }); foreach my $i (0..$#tt) { $pm->start and next; my @r; if (scalar @{$tt[$i]} > 1) { foreach my $j (0..$#tc) { if (scalar @{$tc[$j]} > 1) { push @r, foo(scalar @{$tt[$i]}, scalar @{$tc[$j]}, \@{$tt[$i]}, \@{$tc[$j]}); } else { push @r, $epsilon; } } } else { @r = @nm; } $pm->finish($i, [@r]); undef @r; } $pm->wait_all_children; 设置为0,则该过程完成没有问题,原始C代码在几分钟内完成($cpu_count只有~2秒),但是当FM如果打开,它似乎会持续很长时间。当我输入像sub foo {return rand;}这样的印刷语句来诊断问题时,它似乎确实在运行。

如果我拿出所有与FM相关的代码并尝试使用常规的双foreach循环,那么运行时是一样的。

提前致谢!

2 个答案:

答案 0 :(得分:3)

这是因为从子节点发送到父节点的数据被写入磁盘(请参阅Parallel::ForkManager中的RETRIEVING DATASTRUCTURES):

  

数据结构          在给定的子进程中引用的是序列化的并通过它写出到文件中          耐储藏。随后将该文件读回内存和新的数据结构          属于父进程的创建。请考虑一下表现          它可能意味着惩罚,所以尽量保持返回的结构小。

在调试模式下,不会发生分叉,因此可以直接传递结构而无需保存和加载。

Thread::Queue可能会产生更好的效果。

#!/usr/bin/perl
use strict;
use warnings;

use threads;
use Thread::Queue;

sub read_table {
    map [ map rand, 1 .. 100 ], 1 .. 100;
}
sub foo {
    [ @_ ]
}

my $cpu_count = 20; my $epsilon = 1e-16;
my @tt = read_table('tt.csv');
my @tc = read_table('tc.csv');
my @nm = ($epsilon) x scalar @tc;
my @results;

my ($q_in, $q_out) = map 'Thread::Queue'->new, 1, 2;
my @workers = map threads->create(sub{
    while(defined(my $i = $q_in->dequeue)) {
        warn $i;
        my @r;
        if (scalar @{$tt[$i]} > 1) {
            for my $j (0 .. $#tc) {
                if (scalar @{$tc[$j]} > 1) {
                    push @r, foo(scalar @{$tt[$i]}, scalar @{$tc[$j]}, \@{$tt[$i]}, \@{$tc[$j]});
                } else {
                    push @r, $epsilon; 
                }
            }
        } else {
            @r = @nm;
        }
        $q_out->enqueue([$i, @r]);
    }
}), 1 .. $cpu_count;

$q_in->enqueue(0 .. $#tt);
$q_in->end;


for (0 .. $#tt) {
    my $r = $q_out->dequeue;
    my $i = shift @$r;
    warn "$i: $r->[2][2][1]";
}
$_->join for @workers;

答案 1 :(得分:0)

这是因为你的工作人员做得太少,以至于创建流程的开销更大,将数据传回给父工具的工作量大于实际工作量。

建议:

  • 不是为子项指定@tt的单个元素,而是指定一组元素。
  • 处理父项@{$tt[$i]}为空时的案例。

choroba的解决方案减少了开销,但保持了原始程序的低效率。通过实施我的建议,他们的解决方案可以更快。

顺便说一下,$pm->finish($i, [@r]);最好写成$pm->finish($i, \@r);。无需创建新阵列。