Как я могу заставить свой Perl script использовать несколько ядер для дочерних процессов?

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

Вырезать преследование: у меня есть Perl script, который много раз вызывает XFOIL с разными входными параметрами для генерации необходимых мне данных. Мне нужно, чтобы XFOIL запускался 5600 раз, и, поскольку он стоит прямо сейчас, он занимает около 100 секунд в среднем за каждый ход. Выполняя математику, это означает, что потребуется около 6,5 дней.

Теперь у меня есть четырехъядерная машина, но мой опыт как программиста ограничен, и я действительно знаю только, как использовать базовый Perl. Я хотел бы запускать 4 экземпляра XFOIL за раз, все на своем собственном ядре. Что-то вроде этого:

while (1){
    for (i = 1..4){
        if (! exists XFOIL_instance(i)){
            start_new_XFOIL_instance(i, input_parameter_list);
        }
    }
} 

Итак, программа проверяет (или предпочитает спать до тех пор, пока экземпляр XFOIL не пробудит ее, чтобы запустить новый экземпляр), если в каждом ядре работает XFOIL. Если нет, предыдущий экземпляр вышел, и мы можем запустить новый экземпляр с новым списком входных параметров.

Если кто-нибудь знает, как это может быть достигнуто, пожалуйста, дайте мне знать. Это значительно ускорит время, необходимое мне для создания данных, и позволит мне работать над самим аэрокосмическим проектом.

Спасибо за помощь!

Ответ 1

Попробуйте 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.

Ответ 2

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

www.gearman.org

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

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

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

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

Ответ 3

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

Основные недостатки потоков 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

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

Ответ 5

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