Parallel :: ForkManager使子程序慢1000x

时间:2016-10-31 21:12:40

标签: multithreading perl parallel-processing fork

我有一个子程序,我可以像 I 一样进行串行优化,大致类似

sub overlap {

    my $hash_reference = shift;   # pass the hash to the subroutine
    my %h = %{ $hash_reference }; # refer to the hash as %h
    my $standard = shift;         # this is the key that will be compared against
    my $compared = shift;         # this is the key being compared
    my $start_index = 0;          # this will continually be increased
                                  # to save computation time

    # I want to parallelize here

    foreach my $s ( 0 .. scalar @{ $h{$standard}{end} }-1 ) {
        foreach my $c ( $start_index .. scalar @{ $h{$compared}{end} }-1 ) {
            ... # abbreviated for minimal working example
        }
    }

    return ($standard_indices_met_in_compared, \@overlay);
}

这是一个缓慢的子程序。我在大约12-14分钟内运行了数千次,但是一次又一次地运行会浪费时间。

我经常使用Parallel::ForkManager进行系统处理,但这在这里效果不佳。

Parallel::ForkManager的实施看起来像

use Parallel::ForkManager qw();
my $manager = new Parallel::ForkManager(2);
foreach my $s ( 0 .. scalar @{ $h{$standard}{end} }-1 ) {

    foreach my $c ( $start_index .. scalar @{ $h{$compared}{end} }-1 ) {
        $manager->start and next;
        ... # abbreviated for minimal working example
    }

    $manager->finish;
}

$manager->wait_all_children;      # necessary after all lists

我看过线程等,但是看不到如何在这里申请。

我查看了Perl multithreading and foreach和线程的Perl文档以及其他许多来源,但我不知道如何应用之前已经完成的工作。我看到的一切看起来都只适用于系统命令。

我想写一个共享数组和标量,没有系统命令。如果我遗漏了什么,请告诉我。

如何在子例程中并行化foreach循环?

2 个答案:

答案 0 :(得分:3)

您是否真的尝试仅使用最多两个进程进行并行化?如果是这样,这可能是感知缓慢的原因。

并行化总是会产生开销。如果并行化10个进程,则无法保证10倍的加速。

我建议您将最大数量的流程打开,以便更合理,然后再试一次。如果这没有帮助,可能是由于:

  • 硬件限制
  • 关于你试图并行化的循环,强制顺序执行(例如写入同一个文件,DB表,更新一个semaphored,共享变量......)

答案 1 :(得分:1)

在我们看到Parallel::ForkManager部分后,我想解决所显示内容的直接错误,已在ysth的评论中注明。

为了清晰起见,仅显示循环,并且有更多有意义的限制,您有

use Parallel::ForkManager;
my $manager = Parallel::ForkManager->new(8);

foreach my $s ( ... )
{    
    foreach my $c ( ... ) 
    {
        $manager->start and next;    # 
        # code                       # WRONG
    }                                # Module: Can't fork inside child
    $manager->finish;                #
}
$manager->wait_all_children;

让我们看看这是怎么回事。

孩子在内环中分叉。但它退出之外的,意味着它运行整个循环。因此,每个孩子也会与父母一起执行创建新孩子的行。这将是一个真正的混乱,有一连串的孩子,他们之间的工作分工错误。

但该模块不允许这样做,抛出错误。你的真实代码与展示的不同吗?

现在考虑

foreach my $s ( ... ) 
{    
    $manager->start and next;     # child forked

    foreach my $c ( ... ) 
    {                             # Whole inner loop
        # code                    # run by one child
    }                             # for one value of $s

    $manager->finish;             # child exits
}    

fork发生在内部循环之外,子进程运行整个循环,当前值为$s。父对象跳到外循环的下一次迭代并分叉另一个子进程,该子进程运行内部循环,接下来是$s的值。每个子项运行整个内部循环以获得$s的后续值。所以外循环的迭代是并行执行的。

这就是你想要的。所以改变你的代码来做这件事,看看它是怎么回事。

要重复所说的内容,并非所有代码都能同时并行运行。有些代码根本无法并行运行,有些代码可能会出现明显的性能下降。