【问题标题】:How to run a perl script in parallel如何并行运行 perl 脚本
【发布时间】:2017-04-18 19:25:50
【问题描述】:

如何每次使用不同的输入参数并行运行 perl 脚本:

插图:

perl example.pl param1 param2
perl example.pl param3 param4

我想使用不同的输入 paramsX 运行 perl 脚本 example.pl 2 次或更多次。每次都应该并行运行。

示例算法如下:

my $params='1,2,3,4,5';   
my @all_params = split(/\;/, $params);
foreach my $entry (@all_param)
    {
      perl example.pl $entry
    }

我想为每个循环并行运行 perl 脚本。

【问题讨论】:

标签: perl parallel-processing


【解决方案1】:

您所问的问题看起来很简单,但实际上比看起来要复杂得多。

在 perl 中并行化并不难,但是……这里是龙。当您的程序变得不确定时,并行代码会引入一组全新的错误和竞争条件。您不再能够可靠地知道执行顺序。 (如果你假设你这样做了,你会创建一个竞争条件)。

但考虑到这一点 - 实际上有 3 种(ish?)方法可以解决。

分叉

使用Parallel::ForkManager 并将您的内部循环包含在一个叉子中。这非常适用于“简单”并行性,但在您的分叉之间进行通信很困难。

#!/usr/bin/env perl

use strict;
use warnings;

use Parallel::ForkManager;

my $manager = Parallel::ForkManager->new(2);    #2 concurrent

my $params = '1,2,3,4,5';
my @all_params = split( /,/, $params );

foreach my $entry (@all_param) {
   $manager->start and next;
   #your code to run in parallel here;
   print $entry;
   $manager->finish;
}

您可以使用fork 自行滚动,但您可能会因为这样做而绊倒。所以Parallel::ForkManager 是工作的工具。

线程:

#!/usr/bin/env perl

use strict;
use warnings;

use threads;
use Thread::Queue

  my $work_q = Thread::Queue->new;

sub worker {
   while ( my $item = $work_q->dequeue ) {
      print $item, "\n";
   }
}

my $params = '1,2,3,4,5';
my @all_params = split( /,/, $params );
$work_q->enqueue(@all_params);
$work_q->end;

threads->create( \&worker ) for 1 .. 2;    #2 in parallel
foreach my $thr ( threads->list ) {
   $thr->join;
}

如果您需要做更多的 IPC,这更合适 - 线程(IMO)通常更好。但是,您不应该将线程视为轻量级线程(如分叉),因为尽管您可能会从其他语言中想到 - perl 线程并不是那样工作的。

使用 IO::Select 和多个 open 调用来并行化:

#!/usr/bin/env perl

use strict;
use warnings;

use IO::Select; 

my $params = '1,2,3,4,5';
my @all_params = split( /,/, $params );

foreach my $param ( @all_params ) { 
   open ( my $io, '-|', "program_name $param" ); 
   $select -> add ( $io ); 
}

while ( my $fh = $select -> can_read ) { 
   my $line = <$fh>;
   print $line; 
}      

您可以通过IPC::Run2 执行类似的操作来打开 STDIN 和 STDERR 的文件描述符。

我应该吗?

并行代码不是灵丹妙药。它的作用是减少“块”并让您消耗资源。如果您的限制资源是 CPU,并且您有 10 个 CPU,那么并行使用 10 个 CPU 会加快您的速度。

...但是如果您的限制资源是 IO - 网络或磁盘带宽 - 它通常无济于事,因为争用实际上使问题变得更糟。尤其是磁盘控制器已经非常高效地进行并行化、预取和缓存,因此并行访问它们所获得的收益通常非常微不足道。

【讨论】:

  • 非常感谢@Sobrique 的详细解释,它对理解内在函数有很大帮助。现在我正在使用 ForkManager 方法进行并行构建,它给了我预期的结果。只是为了好奇,是否也可以实现嵌套分叉?我想这样做以使构建更加并行..
  • 是的。您可以再次分叉 - 每次“分叉”时,您都会将进程分成两个具有完全相同状态的相同副本,除了返回码 fork。但是,如果您不小心的话,这样做是一种非常好的方法来实现指数级和“分叉炸弹”。所以最好坚持使用 Parallel::ForkManager。
  • 感谢@Sobrique!
【解决方案2】:

实际上不需要编写任何代码(Perl 或其他)来并行运行您的脚本,您只需使用 GNU Parallel 并控制一次运行多少,脚本有多少不同的服务器运行,结果去向以及几乎任何其他方面。

所以,如果您有一个名为 params.txt 的文件,其中包含:

param1 param2
param3 param4

您可以在终端中执行此操作:

parallel -a params.txt perl {1} {2}

如果你想要一个进度条,只需添加--bar

parallel --bar ...

如果你想一次运行 8 个:

parallel -j 8 ...

如果你想看看它在不实际做任何事情的情况下会做什么:

parallel --dry-run ...

【讨论】:

  • 谢谢马克,是的,我们可以这样做。我通常在直接从 bash 提示符运行脚本时使用这种方式。我想实现的这个用例在 Jenkins2.x 中,我想在其中并行运行一些构建脚本。不幸的是,我在为此目的使用内置的詹金斯“并行”命令时遇到了问题。
猜你喜欢
  • 2010-09-26
  • 2015-02-10
  • 2011-01-01
  • 2011-09-27
  • 1970-01-01
  • 2021-11-16
  • 2016-05-05
  • 1970-01-01
  • 2012-02-13
相关资源
最近更新 更多