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

Привет, у меня есть простой скрипт, который берет файл и запускает на нем другой скрипт Perl. Сценарий делает это для каждого файла изображения в текущей папке. Это работает на машине с 2 четырехъядерными процессорами Xeon, 16 ГБ оперативной памяти, работающей под управлением 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");
     }
}       

person Alos    schedule 13.12.2010    source источник


Ответы (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

     }
}       

Но, как предложил Хугмейр, запускать интерпретатор perl снова и снова для каждого нового файла — не очень хорошая идея.

person gangabass    schedule 13.12.2010
comment
запускать интерпретатор perl снова и снова для каждого нового файла - не очень хорошая идея - да, но когда вы разветвляетесь, вы не запускаете новый интерпретатор perl. Форк копирует родительский процесс, а Linux использует CoW, так что это даже дешевле, чем полная копия. - person JimB; 13.12.2010
comment
Кроме того, почему вы запускаете новый интерпретатор после форка? Запустите код perl в новом дочернем процессе. - person JimB; 13.12.2010
comment
@JimB: я имею в виду, что системный вызов не разветвляется. И я использую системный вызов, потому что его использовал исходный код. - person gangabass; 14.12.2010

Вам следует подумать о том, чтобы НЕ создавать новый процесс для каждого файла, который вы хотите обработать - это ужасно неэффективно и, вероятно, занимает здесь большую часть вашего времени. Просто загрузка Perl и любых модулей, которые вы используете снова и снова, должна создавать некоторые накладные расходы. Я вспоминаю плакат на PerlMonks, который сделал что-то подобное и в итоге преобразовал свой второй скрипт в модуль, сократив время работы с часа до пары минут. Не то, чтобы вы должны были ожидать такого резкого улучшения, но об этом можно мечтать.

Со вторым скриптом, преобразованным в модуль, вот пример использования потока, в котором BrowserUK создает пул потоков, подавая ему задания через очередь.

person Hugmeir    schedule 13.12.2010
comment
Запуск нового интерпретатора perl ужасно неэффективен, но создание нового процесса с помощью fork() выполняется очень быстро (особенно с учетом того, что Linux использует CoW). - person JimB; 13.12.2010
comment
Нет. Если ваша работа будет использовать 1 минуту процессорного времени, время, затрачиваемое на запуск новой задачи, будет незначительным. Perl может использовать, скажем, 1 секунду ЦП для запуска своей среды (если у вас загружено довольно много модулей; я видел это), но после этого он полностью ваш. Внимательно прочитайте вопрос. - person MarkR; 14.12.2010
comment
NB: Perl-потоки — отстой. Действительно, они делают. Они создают множество копий всего (не копии CoW, настоящие копии). В некоторых случаях они работают некорректно, но все равно потребляют кучу ненужных ресурсов. Вилка вместо этого намного эффективнее и с большей вероятностью сработает. - person MarkR; 14.12.2010

  • Импортируйте "maigcplate" и используйте потоки.
  • Запустите magicplate.pl в фоновом режиме (вам нужно будет добавить регулирование процесса)
  • Импортируйте «волшебную пластину» и используйте форк (добавьте дросселирование процессов и детский жнец)
  • Make "maigcplate" a daemon with a pool of workers = # of CPUs
    • use an MQ implementation for communication
    • использовать сокеты для связи
  • Используйте веб-сервер (nginx, apache, ...) и оберните REST для веб-сервиса.
  • так далее...

Все это сосредоточено на создании нескольких рабочих процессов, каждый из которых может работать на своем собственном процессоре. Некоторые реализации будут лучше использовать ресурсы (те, которые не запускают новый процесс), и их будет проще реализовать и поддерживать.

person dietbuddha    schedule 13.12.2010