Как я могу заставить мой скрипт Perl использовать несколько ядер для дочерних процессов? - PullRequest
15 голосов
/ 25 декабря 2009

Я работаю над математической моделью, которая использует данные, сгенерированные из XFOIL, популярного аэрокосмического инструмента, используемого для определения коэффициентов подъема и сопротивления на аэродинамических поверхностях.

У меня есть Perl-скрипт, который многократно вызывает XFOIL с различными входными параметрами для генерации нужных мне данных. Мне нужно, чтобы XFOIL запускался 5600 раз, со скоростью около 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 не освободится, когда мы можем запустить новый экземпляр с новым списком входных параметров.

Ответы [ 5 ]

17 голосов
/ 25 декабря 2009

Попробуйте 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 $!;
}

Вам нужно будет предоставить свои собственные реализации для функций start_new_XFOIL_instance и output_exists, а также вам потребуется определить свои собственные наборы параметровперейти на XFOIL.

4 голосов
/ 19 сентября 2012

Потоки Perl будут использовать преимущества нескольких ядер и процессоров. Основным преимуществом потоков является то, что они довольно легко обмениваются данными между потоками и координируют их действия. Разветвленный процесс не может легко возвращать данные родителю или координировать между собой.

Основные минусы потоков Perl в том, что они относительно дороги в создании по сравнению с форком, они должны копировать всю программу и все ее данные; вы должны собрать их в свой Perl; и они могут быть глючными, чем старше Perl, тем более жгучие потоки. Если ваша работа дорогая, время создания не должно иметь значения.

Вот пример того, как вы можете сделать это с потоками. Есть много способов сделать это, этот использует 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
4 голосов
/ 25 декабря 2009

Похоже, вы можете использовать gearman для этого проекта.

www.gearman.org

Gearman - очередь работ. Вы можете разделить рабочий процесс на множество мини-частей.

Я бы порекомендовал использовать amazon.com или даже их аукционные серверы для завершения этого проекта.

Расход 10 центов за вычислительный час или меньше может значительно ускорить ваш проект.

Я бы использовал gearman локально, убедитесь, что у вас есть "идеальный" пробег для 5-10 ваших подзадач, прежде чем передавать его на вычислительную ферму amazon.

0 голосов
/ 27 июля 2016

Это довольно старый, но если кто-то все еще ищет подходящие ответы на этот вопрос, вы можете рассмотреть Perl Many-Core-Engine (MCE)

0 голосов
/ 18 ноября 2012

Рассматривали ли вы GNU параллельно параллельно . Это позволит вам запустить несколько инсталляционных экземпляров вашей программы с разными входами и заполнить ваши ядра процессора, как только они станут доступны. Часто это очень простой эффективный способ добиться распараллеливания простых задач.

...