Требуемая команда PDL - indadd
.(Спасибо Крису Маршаллу, PDL Pumpking, за указание на это в другом месте .)
PDL разработан для того, что я называю «векторизованными» операциями.По сравнению с операциями C, операции Perl довольно медленные, поэтому вы хотите свести к минимуму количество вызовов метода PDL и заставить каждый вызов выполнять большую работу.Например, этот тест позволяет указать количество обновлений, которые нужно выполнить за один раз (в качестве параметра командной строки).Сторона perl должна выполнять цикл, но сторона PDL выполняет только пять или около того вызовов функций:
use PDL;
use Benchmark qw/cmpthese/;
my $updates_per_round = shift || 1;
my $N = 1_000_000;
my @perl = (0 .. $N - 1);
my $pdl = zeroes $N;
cmpthese(-1,{
perl => sub{
$perl[int(rand($N))]++ for (1..$updates_per_round);
},
pdl => sub{
my $to_update = long(random($updates_per_round) * $N);
indadd(1,$to_update,$pdl);
}
});
Когда я запускаю это с аргументом 1, я получаю еще худшую производительность, чем при использовании set
,это то, что я ожидал:
$ perl script.pl 1
Rate pdl perl
pdl 21354/s -- -98%
perl 1061925/s 4873% --
Это много земли, чтобы придумать!Но держись там.Если мы делаем 100 итераций за раунд, мы получаем улучшение:
$ perl script.pl 100
Rate pdl perl
pdl 16906/s -- -18%
perl 20577/s 22% --
И с 10 000 обновлений за раунд, PDL превосходит Perl в четыре раза:
$ perl script.pl 10000
Rate perl pdl
perl 221/s -- -75%
pdl 881/s 298% --
PDL продолжаетВыполните примерно в 4 раза быстрее, чем обычный Perl для еще больших значений.
Обратите внимание, что производительность PDL может ухудшиться для более сложных операций.Это потому, что PDL будет выделять и разбирать большие, но временные рабочие пространства для промежуточных операций.В этом случае вы можете рассмотреть возможность использования Inline::Pdlpp
.Однако это не инструмент для начинающих, поэтому не прыгайте туда, пока не определите, что это действительно лучшая вещь для вас.
Другая альтернатива всему этому - использовать Inline::C
, например, так:
use PDL;
use Benchmark qw/cmpthese/;
my $updates_per_round = shift || 1;
my $N = 1_000_000;
my @perl = (0 .. $N - 1);
my $pdl = zeroes $N;
my $inline = pack "d*", @perl;
my $max_PDL_per_round = 5_000;
use Inline 'C';
cmpthese(-1,{
perl => sub{
$perl[int(rand($N))]++ for (1..$updates_per_round);
},
pdl => sub{
my $to_update = long(random($updates_per_round) * $N);
indadd(1,$to_update,$pdl);
},
inline => sub{
do_inline($inline, $updates_per_round, $N);
},
});
__END__
__C__
void do_inline(char * packed_data, int N_updates, int N_data) {
double * actual_data = (double *) packed_data;
int i;
for (i = 0; i < N_updates; i++) {
int index = rand() % N_data;
actual_data[index]++;
}
}
Для меня функция Inline постоянно превосходит как Perl, так и PDL.При больших значениях $updates_per_round
, скажем 1000, я получаю версию Inline::C
примерно в 5 раз быстрее, чем чистый Perl, и в 1,2 - 2 раза быстрее, чем PDL.Даже когда $updates_per_round
равен только 1, а Perl легко превосходит PDL, встроенный код в 2,5 раза быстрее, чем код Perl.
Если это все , вам нужно выполнить, я рекомендуюиспользуя Inline::C
.
Но если вам нужно выполнить много манипуляций с вашими данными, вам лучше всего использовать PDL для его мощности, гибкости и производительности.Ниже описано, как вы можете использовать vec()
с данными PDL.