我该如何修改我的Perl脚本以使用多个处理器?

6

你好,我有一个简单的脚本,它接受一个文件并在其上运行另一个Perl脚本。该脚本对当前文件夹中的每个图片文件执行此操作。这在一台带有2个四核Xeon处理器、16GB内存的RedHat Linux机器上运行。

第一个脚本work.pl基本上调用了magicplate.pl并传递了一些参数和要处理的文件名给magicplate.pl。 Magic Plate需要大约一分钟才能处理完每个图像。由于work.pl正在执行相同的功能超过100次,并且因为系统有多个处理器和内核,所以我考虑将任务分割成多个部分,以便可以并行运行多次。如果必要,我可以将图像拆分到不同的文件夹中。任何帮助都将非常感激。谢谢

以下是目前的进展:

use strict;
use warnings;


my @initialImages = <*>;

foreach my $file (@initialImages) {

    if($file =~ /.png/){
        print "processing $file...\n";
        my @tmp=split(/\./,$file);
        my $name="";
        for(my $i=0;$i<(@tmp-1);$i++) {
            if($name eq "") { $name = $tmp[$i]; } else { $name=$name.".".$tmp[$i];}
        }

        my $exten=$tmp[(@tmp-1)];
        my $orig=$name.".".$exten;

        system("perl magicPlate.pl -i ".$orig." -min 4 -max 160 -d 1");
     }
}       
3个回答

7
你应该考虑不为每个要处理的文件创建一个新进程--这非常低效,可能是你在这里花费大部分时间的原因。反复加载Perl和你使用的模块应该会产生一些开销。我记得PerlMonks上有一个发帖者做了类似的事情,最终将他的第二个脚本转换成了一个模块,将工作时间从一个小时减少到了几分钟。虽然你不应该期望有如此戏剧性的改善,但人们总可以梦想。
通过将第二个脚本重构为一个模块,这里有一个线程使用的示例,其中BrowserUK创建了一个线程池,通过队列向其提供作业。

5
启动新的 Perl 解释器效率极低,但使用 fork() 创建新进程非常快(特别是因为 Linux 使用 CoW)。 - JimB
2
不会。如果你的工作将使用1分钟的CPU时间,启动新任务所花费的时间将是相当可忽略的。Perl可能会使用大约1秒钟的CPU来启动其环境(如果你加载了相当多的模块;我见过这种情况),但之后,它就全部归你了。仔细阅读问题。 - MarkR
1
注:Perl线程很差。真的,它们会创建大量的副本(不是写时复制,而是真正的副本)。在某些情况下不工作,但仍然使用大量不必要的资源。相反,使用Fork,它更有效率,也更有可能成功。 - MarkR

3
您可以使用Parallel::ForkManager(将$MAX_PROCESSES设置为同时处理的文件数):
use Parallel::ForkManager;
use strict;
use warnings;

my @initialImages = <*>;

foreach my $file (@initialImages) {

    if($file =~ /.png/){
        print "processing $file...\n";
        my @tmp=split(/\./,$file);
        my $name="";
        for(my $i=0;$i<(@tmp-1);$i++) {
            if($name eq "") { $name = $tmp[$i]; } else { $name=$name.".".$tmp[$i];}
        }

        my $exten=$tmp[(@tmp-1)];
        my $orig=$name.".".$exten;

  $pm = new Parallel::ForkManager($MAX_PROCESSES);
    my $pid = $pm->start and next;
        system("perl magicPlate.pl -i ".$orig." -min 4 -max 160 -d 1");
    $pm->finish; # Terminates the child process

     }
}       

然而,正如Hugmeir所建议的那样,为每个新文件再次运行Perl解释器并不是一个好主意。


2
反复为每个新文件运行perl解释器并不是一个好主意。- 是的,但是当您进行fork操作时,您并没有启动一个新的perl解释器。Fork会复制父进程,并且Linux使用写时复制(CoW)技术,因此它比完全复制还要更便宜。 - JimB
2
另外,为什么你在 fork 之后要启动一个新的解释器?在新的子进程中运行 Perl 代码。 - JimB
@JimB:我的意思是系统调用而不是分叉。我使用系统调用是因为原始代码也使用了它。 - gangabass

3
  • 导入"maigcplate"并使用线程。
  • 在后台启动magicplate.pl(您需要添加进程限制)
  • 导入"magicplate"并使用fork(添加进程限制和kiddy reaper)
  • 将"maigcplate"设置为守护程序,具有与CPU数量相同的工作池
    • 使用MQ实现通信
    • 使用套接字进行通信
  • 使用Web服务器(nginx,apache等)并包装REST以进行Web服务
  • 等等...

所有这些都围绕着创建多个可以在自己的CPU上运行的工作程序。某些实现将更好地使用资源(那些不启动新进程的实现),并且更易于实现和维护。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接