Использование потоков для анализа последовательности ДНК с помощью Perl

У меня есть пример последовательности ДНК, такой как: S = ATGCGGGCGTGCTGCTGGGCTGCT.... длиной 5 МБ. Кроме того, у меня есть координаты гена для каждого гена, например:

Gene no. Start End
1          1    50
2         60    100
3         110   250
.....
4000      4640942 4641628 

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

Вот краткое изложение моего кода:

foreach my $gene($sequence){
     my @coordinates = split("\t",$gene);
     $model1 = substr($sequence, $coordinates[1], 50);
     $model2 = substr($sequence, $coordinates[1], 60);
     $c-value = calculate($model1, $model2);
     ....
}

sub calculate {
     ......
}

Я был бы очень признателен, если бы кто-нибудь мог предложить мне, как распараллелить такие программы. Что я хочу распараллелить, так это вычисление c-value между model1 и model2 для каждого гена, что в итоге ускорит процесс. Я пытался использовать Threads::queue, но закончился кучей ошибок. Я новичок в программировании на Perl, поэтому любая помощь приветствуется.

Спасибо всем за комментарии и предложения. Я изменил код, и, похоже, он работает с использованием Perl-модуля Parallel::ForkManager. Код успешно использует все 4 ядра моего компьютера.

Вот измененный код:

    use strict;
    use warnings;
    use Data::Dumper;
    use Parallel::ForkManager;
    my $threads = 4;
    my $pm = new Parallel::ForkManager($threads);
    my $i = 1; #gene number counter
    $pm -> run_on_finish( sub { $i++; print STDERR "Checked $i genes" if ($i % $number_of_genes == 0); } ); 
    my @store_c_value = ();
    foreach my $gene($sequence){
                 my $pid = $pm->start and next;
                 my @coordinates = split("\t",$gene);
                 my $model1 = substr($sequence, $coordinates[1], 50);
                 my $model2 = substr($sequence, $coordinates[1], 60);
                 my $c-value = calculate($model1, $model2);
                 push(@store_c_value, $c-value);
                 $i++;
                 $pm->finish;
            }
    $pm->wait_all_children;
            sub calculate {
                 ......
                 return ($c-value);
            }
    print Dumper \@store_c_value;

Текущая проблема заключается в том, что я не получаю никакого вывода для @store_c_value (т.е. пустой массив). Я обнаружил, что вы не можете сохранить данные из дочернего процесса в массив, который был объявлен в основной программе. Я знаю, что могу распечатать его во внешний файл, но я хочу, чтобы эти данные были в массиве @store_c_value, так как я буду использовать его позже в программе.

Еще раз спасибо за помощь.


person Soham Sengupta    schedule 28.10.2018    source источник
comment
Попробуйте AnyEvent::Fork metacpan.org/pod/AnyEvent::Fork.   -  person UjinT34    schedule 28.10.2018
comment
Первым шагом любой оптимизации является измерение. Затем используйте профилировщик, чтобы определить, сколько времени и где именно тратится. Тогда и только тогда подумайте о различных подходах к оптимизации.   -  person Ulrich Eckhardt    schedule 28.10.2018
comment
Пожалуйста, покажите больше своего кода, не все в теге perl знакомы с биоинформатикой, но все равно могут помочь вам с распараллеливанием, если дать больше деталей.   -  person Håkon Hægland    schedule 28.10.2018
comment
Одна ошибка может быть связана с тем, что $c-value не является допустимым идентификатором Perl. Если вы используете строгие и предупреждения (вы должны), это вызовет ошибку из-за значения bareword. В идентификаторах Perl допустимы только символы слова (буквы, цифры, знак подчеркивания).   -  person Grinnz    schedule 28.10.2018
comment
Кроме того, foreach my $gene($sequence){ не имеет смысла, он будет выполняться только один раз, потому что вы указали одно скалярное значение для его повторения.   -  person Grinnz    schedule 28.10.2018
comment
Спасибо за предложения и комментарии. Я изменил код. Пожалуйста, смотрите, я отредактировал основной пост.   -  person Soham Sengupta    schedule 29.10.2018
comment
Нет, вы не можете напрямую писать от ребенка к родителю. Полные примеры того, как передавать данные обратно с помощью Parallel::ForkManager, см. в этой публикации (и ссылке в ней, и связанной документации).   -  person zdim    schedule 29.10.2018
comment
В вашем коде все те же проблемы, на которые я указал. Parallel::ForkManager и IO::Async::Function предоставляют механизмы для возврата результата дочернего процесса родительскому.   -  person Grinnz    schedule 29.10.2018


Ответы (1)


Одним из вариантов является IO::Async::Function, который будет использовать форки или потоки в зависимости от того, на какой ОС вы работаете (разветвление гораздо более эффективно в системах Unixy), и поддерживать набор рабочих процессов/потоков для параллельного запуска кода. Он возвращает экземпляры Future, которые можно использовать для синхронизации асинхронного кода по мере необходимости. Есть много способов использовать Future, пара представлена ​​ниже.

use strict;
use warnings;
use IO::Async::Loop;
use IO::Async::Function;
use Future;

my $loop = IO::Async::Loop->new;
# additional options can be passed to the IO::Async::Function constructor to control how the workers are managed
my $function = IO::Async::Function->new(code => \&calculate);
$loop->add($function);

my @futures;
foreach my $gene($sequence){
     my @coordinates = split("\t",$gene);
     my $model1 = substr($sequence, $coordinates[1], 50);
     my $model2 = substr($sequence, $coordinates[1], 60);
     push @futures, $function->call(args => [$model1, $model2])->on_done(sub {
         my $c_value = shift;
         # further code using $c_value must be here, to be run once the calculation is done
     })->on_fail(sub {
         warn "Error in calculation for $gene: $_[0]\n";
     });
}

# wait for all calculations and on_done handlers before continuing
Future->wait_all(@futures)->await;

Если вы хотите, чтобы программа немедленно остановилась, если в одном из вычислений возникает исключение, вы можете использовать need_all и удалить отдельные обработчики on_fail, а также использовать get, который является оболочкой await, который затем вернет все c-значения по порядку. если они преуспевают, или выбрасывают исключение в случае сбоя.

my @c_values = Future->needs_all(@futures)->get;
person Grinnz    schedule 28.10.2018