Как использовать потоки для замены цикла подпрограммы в perl/pdl
У меня есть очень хорошая подпрограмма Perl, написанная как часть модуля Perl. Не вдаваясь в подробности, он принимает строку и короткий список в качестве аргументов (часто берется из терминала) и выдает значение (прямо сейчас, всегда с плавающей запятой, но это не всегда так).
Прямо сейчас, часть списка моего аргумента принимает два значения, скажем (val1,val2). Я сохраняю вывод моей подпрограммы для сотен различных значений для val1 и val2, используя циклы for. Каждая итерация занимает почти секунду, поэтому весь этот процесс занимает часы.
Недавно я читал о мистическом (для меня) вычислительном инструменте, называемом "многопоточность", который, очевидно, может заменить циклы с невероятно быстрым временем выполнения. У меня были проблемы с пониманием, что это такое и что они делают, но я думаю, что они как-то связаны с параллельными вычислениями (и я хотел бы, чтобы мой модуль был максимально оптимизирован для параллельных процессоров).
Если я сохраню все значения, которые я хотел бы передать в val1 в виде списка, скажем, @val1 и то же самое для val2, как я могу использовать эти "потоки" для выполнения моей подпрограммы для каждой комбинации элементов val1 и val2? Кроме того, было бы полезно узнать, как обобщить эту процедуру для подпрограммы, которая также принимает val3, val4 и т. Д.
2 ответа
Обновить:
Я не использую PDL, поэтому я не знал, что поток в PDL не соответствует в точности понятию потоков, о котором я говорил. Смотрите разделы PDL и подписи:
Сначала мы должны объяснить, что мы подразумеваем под многопоточностью в контексте PDL, тем более что термин "многопоточность" уже имеет особое значение в компьютерной науке, что лишь частично согласуется с его использованием в PDL.
Тем не менее, я думаю, что приведенное ниже объяснение по-прежнему полезно для вас, так как нужно знать, что такое обычные потоки, чтобы понять, чем отличаются потоки PDL.
Вот запись темы в Википедии для фона.
Использование потоков не может сделать вашу программу волшебно быстрее. Если у вас есть несколько процессоров / ядер и если выполняемые вами вычисления можно разделить на независимые фрагменты, использование потоков может позволить вашей программе выполнять более одного вычисления за раз и сократить общее время выполнения.
Самый простой случай, когда подзадачи смущающе параллельны, не требуя связи / координации между потоками.
Что касается возможного увеличения производительности, рассмотрим следующую программу:
#!/usr/bin/perl
use strict; use warnings;
use threads;
my ($n) = @ARGV;
my @threads = map { threads->create(\&act_busy) } 1 .. $n;
$_->join for @threads;
sub act_busy {
for (1 .. 10_000_000) {
my $x = 2 * 2;
}
}
На моем двухъядерном ноутбуке под управлением Windows XP:
C: \> время это t.pl 1 Время: истекшее время: 00: 00: 02.375
C: \> время это t.pl 2 Время: прошедшее время: 00: 00: 02.515
C: \> время это t.pl 3 TimeThis: Elapsed Time: 00: 00: 03.734
C: \> время это т.пл 4 TimeThis: Elapsed Time: 00: 00: 04.703
...
C: \> время это t.pl 10 TimeThis: Elapsed Time: 00:00:11.703
Теперь сравните это с:
#!/usr/bin/perl
use strict; use warnings;
my ($n) = @ARGV;
act_busy() for 1 .. $n;
sub act_busy {
for (1 .. 10_000_000) {
my $x = 2 * 2;
}
}
C: \> время это с.пл 10 Время: истекшее время: 00:00:22.312
Как говорит Синан, «поточность», о которой вы, вероятно, думали, — это «потоки PDL», теперь переименованная (начиная с версии 2.075) в «широковещательную рассылку», чтобы соответствовать общей терминологии (см. документы ). Это позволяет вам заменить что-то вроде этого:
$x = sequence(5);
$x->set($_, $x->at($_)+2) for 0..$x->dim(0)-1;
именно с этим, поскольку "+=" в основном работает с одной вещью (нульмерным скаляром), поэтому с большим количеством измерений, чем скаляр (например, эта одномерная последовательность), он может "транслировать":
$x += 2; # does whole ndarray at once
Это также быстрее, потому что в отличие от
for
цикла, ему не нужно постоянно покидать и снова входить в среду Perl (также известную как «Perl-land»), но он может оставаться в чрезвычайно быстрой «C-land» для выполнения вычислений без накладных расходов.
Мотивация его первоначального названия заключалась в том, что все эти «транслируемые» вычисления независимы и, следовательно, «досадно параллельны», поэтому их можно автоматически распараллеливать. См. документ . Начиная с версии 2.059, PDL по умолчанию устанавливает автоматическую параллельную обработку в зависимости от количества доступных ядер ЦП.