Il comando PDL desiderato è indadd
. (Grazie a Chris Marshall, PDL Pumpking, per avermelo fatto notare elsewhere.)
PDL è progettato per quelle che io chiamo operazioni "vettorializzate". Rispetto alle operazioni C, le operazioni di Perl sono piuttosto lente, quindi è necessario mantenere il numero di invocazioni del metodo PDL al minimo e fare in modo che ciascuna invocazione faccia molto lavoro. Ad esempio, questo benchmark consente di specificare il numero di aggiornamenti da eseguire in un unico passaggio (come parametro della riga di comando). Il lato perl deve loop, ma il lato Pdl esegue solo cinque o giù di chiamate di funzione:
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);
}
});
Quando eseguo questo con un argomento di 1, ottengo prestazioni ancora peggio di quando si utilizza set
, che è quello che ho previsto:
$ perl script.pl 1
Rate pdl perl
pdl 21354/s -- -98%
perl 1061925/s 4873% --
Questo è un sacco di terreno da inventare! Ma aspetta lì. Se facciamo 100 iterazioni per ogni turno, si ottiene un miglioramento:
$ perl script.pl 100
Rate pdl perl
pdl 16906/s -- -18%
perl 20577/s 22% --
E con 10.000 aggiornamenti per ogni turno, Pdl sorpassa Perl di un fattore di quattro:
$ perl script.pl 10000
Rate perl pdl
perl 221/s -- -75%
pdl 881/s 298% --
PDL continua a svolgere circa 4 volte più veloce del semplice Perl per valori ancora più grandi.
Si noti che le prestazioni del PDL possono peggiorare per operazioni più complesse. Questo perché PDL alloca e abbatte spazi di lavoro ampi ma temporanei per le operazioni intermedie. In tal caso, potresti prendere in considerazione l'utilizzo di Inline::Pdlpp
. Tuttavia, non è uno strumento per principianti, quindi non saltare lì finché non hai determinato che è davvero la cosa migliore per te.
Un'altra alternativa a tutto questo è quello di utilizzare Inline::C
in questo modo:
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]++;
}
}
Per me, la funzione Inline batte costantemente sia Perl e Pdl. Per i valori più ampi di $updates_per_round
, ad esempio 1.000, ottengo la versione di Inline::C
circa 5 volte più veloce del Perl puro e tra 1,2x e 2 volte più veloce di PDL. Anche quando $updates_per_round
è solo 1, dove Perl batte maneggevolmente PDL, il codice Inline è 2,5 volte più veloce del codice Perl.
Se questo è tutto il è necessario eseguire, mi consiglia di utilizzare Inline::C
.
Ma se è necessario eseguire molte manipolazioni sui dati, è meglio attenersi al PDL per potenza, flessibilità e prestazioni. Vedi sotto come utilizzare vec()
con dati PDL.
hai guardato i moduli perl bioinformatici? – AlfredoVR