如何让我的 Perl 脚本在子进程中使用多个核心?

16

我正在开发一个数学模型,该模型使用从XFOIL生成的数据,XFOIL是一种流行的航空工具,用于查找翼型上的升力和阻力系数。

我有一个Perl脚本,它使用不同的输入参数重复调用XFOIL以生成所需的数据。我需要运行5,600次XFOIL,每次运行大约100秒,因此需要大约6.5天才能完成。

我有一台四核机器,但作为程序员,我的经验有限,我只知道如何使用基本的Perl。

我想同时运行四个XFOIL实例,每个实例都在自己的核心上运行。就像这样:

while ( 1 ) {

    for ( i = 1..4 ) {

        if ( ! exists XFOIL_instance(i) ) {

            start_new_XFOIL_instance(i, input_parameter_list);
        }
    }
} 

所以程序会检查(或更好的是休眠),直到有一个空闲的XFOIL实例,这时我们可以使用新的输入参数列表启动新实例。

1
很抱歉我不能提供完整的答案,但简短的版本是你绝对可以分叉出四个当前perl脚本的实例,然后让每个实例不断地外壳运行XFOIL脚本。然而,为生成的进程设置处理器亲和性——这需要知道你正在使用哪个操作系统。 - Conspicuous Compiler
3
你确定XFOIL没有使用多处理器或线程来在第一次运行时将其运行时间缩短至约100秒吗? - dlamblin
将XFOIL实现到C/Fortran中难吗?如果不难,那我建议你去尝试一下。Perl并不是编程语言中的“Speedy Gonzalez”(指速度非常快的人或物)。 - Zaid
谢谢大家迄今为止的评论。@Conspicuous Compiler:我正在运行Ubuntu 9.10。@dlamblin:检查系统监视器显示XFOIL只使用了1个核心。@Zaid:XFOIL是用FORTRAN编写的。Perl脚本只是对它进行system()调用。@Idigas:请参阅上面的评论。还要注意,对于典型的AOA范围(+/-10),它非常快,但我的项目的典型AOA摆动范围为+/-40。 - Dang Khoa
如果您生成了几个子进程,操作系统本身将免费为您安排它们在CPU之间的调度。 - el.pescado - нет войне
5个回答

17

尝试使用Parallel::ForkManager。这是一个提供类似此类进程分离的简单接口的模块。

这里是一些示例代码:

#!/usr/bin/perl

use strict;
use warnings;
use Parallel::ForkManager;

my @input_parameter_list = 
    map { join '_', ('param', $_) }
    ( 1 .. 15 );

my $n_processes = 4;
my $pm = Parallel::ForkManager->new( $n_processes );
for my $i ( 1 .. $n_processes ) {
    $pm->start and next;

    my $count = 0;
    foreach my $param_set (@input_parameter_list) {         
        $count++;
        if ( ( $count % $i ) == 0 ) {
            if ( !output_exists($param_set) ) {
                start_new_XFOIL_instance($param_set);
            }
        }
    }

    $pm->finish;
}
$pm->wait_all_children;

sub output_exists {
    my $param_set = shift;
    return ( -f "$param_set.out" );
}

sub start_new_XFOIL_instance {
    my $param_set = shift;
    print "starting XFOIL instance with parameters $param_set!\n";
    sleep( 5 );
    touch( "$param_set.out" );
    print "finished run with parameters $param_set!\n";
}

sub touch {
    my $fn = shift;
    open FILE, ">$fn" or die $!;
    close FILE or die $!;
}
你需要提供自己的实现来启动新的 XFOIL 实例并检查输出是否存在。你还需要定义自己的参数集以传递给 XFOIL。

1
这看起来是我需要的。我会研究一下Parallel::ForkManager并告诉你它的进展如何。感谢你的帮助!当然,任何其他人的意见都会受到欢迎。 - Dang Khoa
如果你还不知道,你可以在你的主目录中安装Parallel::ForkManager模块。请参考以下链接了解如何操作: https://dev59.com/CHRB5IYBdhLWcg3wtZQV - James Thompson
1
James,非常感谢你的帮助。我刚刚通过命令行安装了Parallel::ForkManager - 我想现在我已经可以使用它了。我仍在尝试弄清楚模块的复杂性以及在错误条件下如何使其运行,但是在我的双核笔记本电脑上进行的初步运行让我认为我已经解决了这个问题 - 至少是基本思路。再次感谢你! - Dang Khoa
Parallel::ForkManager在Windows上使用多核吗?我在我的Windows机器上尝试了一下,它确实使用了很多线程。请原谅我的无知,但是在Windows上,同一进程的线程是否会跨多个核心运行? - Matthew Lock

5

Perl线程可以利用多个核心和处理器。线程的主要优点是它相对容易在线程之间共享数据并协调它们的活动。一个分叉进程不能轻松地将数据返回给父进程,也不能在它们之间进行协调。

Perl线程的主要缺点是与分叉相比创建它们相对昂贵,必须复制整个程序和所有数据;您必须将它们编译到您的Perl中;并且它们可能存在漏洞,Perl越旧,线程就越有bug。如果您的工作很昂贵,则创建时间不应该成为问题。

以下是如何使用线程的示例。有许多方法可以做到这一点,此示例使用Thread::Queue创建一个大型工作列表,您的工作线程可以共享。当队列为空时,线程退出。其主要优点是更容易控制活动线程的数量,并且您不必为每个工作创建一个新的昂贵线程。

这个例子一次性将所有工作都放入队列中,但是您也可以随时向队列中添加任务。如果您这样做,您将使用dequeue而不是dequeue_nb,它会等待更多的输入。
use strict;
use warnings;

use threads;
use Thread::Queue;

# Dummy work routine
sub start_XFOIL_instance {
    my $arg = shift;
    print "$arg\n";
    sleep 1;
}

# Read in dummy data
my @xfoil_args = <DATA>;
chomp @xfoil_args;

# Create a queue to push work onto and the threads to pull work from
# Populate it with all the data up front so threads can finish when
# the queue is exhausted.  Makes things simpler.
# See https://rt.cpan.org/Ticket/Display.html?id=79733
my $queue = Thread::Queue->new(@xfoil_args);

# Create a bunch of threads to do the work
my @threads;
for(1..4) {
    push @threads, threads->create( sub {
        # Pull work from the queue, don't wait if its empty
        while( my $xfoil_args = $queue->dequeue_nb ) {
            # Do the work
            start_XFOIL_instance($xfoil_args);
        }

        # Yell when the thread is done
        print "Queue empty\n";
    });
}

# Wait for threads to finish
$_->join for @threads;

__DATA__
blah
foo
bar
baz
biff
whatever
up
down
left
right

我看到我的先前评论(或者你的先前回答)已经被删除了,不管怎样感谢你更新了你的回答。 我很好奇,如果您验证了线程可以利用多个核心和处理器,那么您是如何验证的呢?谢谢 =) - user454322
看到你的评论后,我写了一个小脚本,在一堆线程中执行无限循环,并使用 OS X 上的 Activity Monitor 查看所有四个核心都在使用。你说的关于线程模型是每个真实线程一个新的 Perl 解释器是正确的。之前我一直认为它是在单个进程中模拟完成的。 - Schwern
我已经发布了https://dev59.com/l2jWa4cB1Zd3GeqPtblp,如果你有机会,请看一下。 - user454322

4
这似乎可以使用Gearman来完成这个项目。
www.gearman.org
Gearman是一个作业队列。您可以将工作流程拆分为许多小部分。
我建议使用amazon.com甚至他们的拍卖服务器来完成这个项目。
每小时花费10美分或更少的计算时间,可以显着加快您的项目。
我会在本地使用Gearman,在将其移交给Amazon计算农场之前,请确保您的5-10个子作业运行“完美”。

0

虽然这篇文章有些老旧,但如果仍有人在寻找适当的答案,您可能想考虑使用Perl Many-Core-Engine (MCE)


0
你考虑过使用 GNU Parallel parallel 吗?它可以让你运行多个程序实例,每个实例使用不同的输入,并在 CPU 核心可用时填充它们。这通常是一种非常简单和高效的方式来实现简单任务的并行化。

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