Perl Parallel::ForkManager ,fork 条件改变需要很长时间

Posted

技术标签:

【中文标题】Perl Parallel::ForkManager ,fork 条件改变需要很长时间【英文标题】:Perl Parallel::ForkManager , takes long time with fork condition changed 【发布时间】:2015-04-20 06:02:46 【问题描述】:

在修改 pid 条件时,我对 Parallel::ForkManager 有两个问题。

问题1:举个简单的例子,

use strict;
use warnings;
use Parallel::ForkManager;
use IO::Socket ;

my $np = 32 ;
my $pm = Parallel::ForkManager->new($np);
$| = 1 ;

sub do_something #an example for 3 second delay
    my $port = shift @_ ;
    my $tgt = 192.168.0.1 ;
    my $sock = IO::Socket::INET->new(PeerAddr=>"$tgt:$port",Proto='tcp',Timeout=>3) ;
    $sock ? return $sock : return 0 ;


foreach (1..64) 
    $pm->start and next ;
    do_something($_) ;
    $pm->finish ;

$pm->wait_all_children ;

上面的脚本工作,与

time perl simple_script.pl 

real    0m7.109s
user    0m0.237s
sys     0m0.080s

同时修改

$pm->start and next ; 
do_something($_) ;

$pm->start and next or do_something($_) ;

!($pm->start) or do_something($_) ; 

似乎也可以工作,但不如预期的那样。

time perl modified_script.pl 

real     3m12.307s
user     0m0.237s
sys      0m0.080s

似乎其他两个修改版本一次运行一个进程。

问题2:

有没有其他方法可以在不使用 foreach 循环中的 next 的情况下做同样的事情?这样脚本就可以适应 map 而不是 foreach 循环。

P.S:分叉和对象创建不是我的强项。

编辑:根据建议删除 &。

【问题讨论】:

最大的问题是为什么要使用map,它旨在通过对输入列表的每个元素应用相同的函数来将一个列表转换为另一个列表。顺便说一句,在调用 Perl 函数时不要使用 & 符号 &:从大约 20 年前的 Perl 4 开始就没有必要了 【参考方案1】:

如果你改变这个循环

foreach (1..64) 
  $pm->start and next;
  do_something($_);
  $pm->finish;

到这里

foreach (1..64) 
  !($pm->start) or do_something($_);

然后您删除了终止每个孩子的$pm->finish 调用。这意味着子进程将继续执行for 循环,尝试启动自己的子进程,这将失败(因为子进程需要自己的Parallel::ForkManager 对象-它们不能使用父进程),因此它们将@987654326 @ 每次循环。此外,由于子程序在执行子例程时被延迟了很多次,因此启动所有 64 个子程序将花费更长的时间,因为您每次运行的限制为 32 个。

你可以写

for ( 1 .. 64 ) 
  my $pid = $pm->start;
  if ( $pid == 0 ) 
    do_something($_);
    $pm->finish;
  


$pm->wait_all_children;

但正如我在评论中所说,如果这只是为了让您可以滥用map 而不是使用for,那么请不要这样做

【讨论】:

感谢您的回复,但我没有删除 $pm->finish ;我的代码是 !($pm->start) 或 do_something($_) ; $pm->完成;但现在我明白逻辑哪里出错了。 $pm->finish 的执行与 do_something($_) 执行无关。 @vagrant:希望你能解释一下为什么要使用map 我会尽量简短,我试图使用 perl 作为总结脚本。基本上我在fortran(和一些C编写的)中有一些函数,它在一组矩阵上进行一些正交积分和矩阵运算。这些矩阵是通过改变单个参数生成的,因此很容易将参数的范围视为一个列表以及作用于该列表的一系列操作。在那时,将操作作为地图管道传递似乎是合乎逻辑的。所以认为将 fork 与地图一起使用会很好 - 可能不是!。【参考方案2】:

您声称所做的更改不会导致该过程花费更长的时间。因此,我将专注于您的问题:如何将 foreach 循环转换为 map 循环。

for (LIST) BLOCK 可以写成map BLOCK LIST。问题是你不能使用next,因为循环的每一次传递都应该返回一些东西(如果只有空列表的话)。

my $pm = ...;
map 
   if (!$pm->start) 
      do_something($_);
      $pm->finish;
   
   ()
 1..64;
$pm->wait_all_children();

sub child 
   my ($job) = @_;
   ...
   exit();



   my $pm = ...;
   map  $pm->start or child($_)  1..64;
   $pm->wait_all_children();

我不知道你为什么要这么做。它只会使代码更难阅读。

【讨论】:

以上是关于Perl Parallel::ForkManager ,fork 条件改变需要很长时间的主要内容,如果未能解决你的问题,请参考以下文章

Perl - 如何查看Perl模块路径

Perl 之父同意 Perl 6 改名为 Raku

以后没有 Perl 6 了!Perl 之父同意改名

Perl基础速成

Perl 的 rpm 版本不同于“perl -v”

Perl语言入门