El comando PDL desea es indadd
. (Gracias a Chris Marshall, PDL Pumpking, por señalarlo me elsewhere.)
PDL está diseñado para lo que llamo "operaciones vectorizadas". En comparación con las operaciones en C, las operaciones de Perl son bastante lentas, por lo que debe mantener el número de invocaciones al método PDL al mínimo y hacer que cada invocación haga mucho trabajo. Por ejemplo, este punto de referencia le permite especificar el número de actualizaciones para realizar de una sola vez (como un parámetro de línea de comandos). El lado Perl tiene que bucle, pero el lado PDL sólo realiza cinco o así llamadas a funciones:
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);
}
});
Cuando ejecuto esto con un argumento de 1, I empeorar aún más el rendimiento que cuando se utiliza set
, que es lo que yo esperado:
$ perl script.pl 1
Rate pdl perl
pdl 21354/s -- -98%
perl 1061925/s 4873% --
¡Eso es mucho terreno para recuperar! Pero espera ahí. Si hacemos 100 iteraciones por ronda, se obtiene una mejora:
$ perl script.pl 100
Rate pdl perl
pdl 16906/s -- -18%
perl 20577/s 22% --
Y con 10.000 actualizaciones por ronda, PDL supera a Perl en un factor de cuatro:
$ perl script.pl 10000
Rate perl pdl
perl 221/s -- -75%
pdl 881/s 298% --
PDL sigue llevando a cabo más o menos 4 veces más rápido que Perl simple para valores aún mayores.
Tenga en cuenta que el rendimiento de PDL puede degradarse para operaciones más complejas. Esto se debe a que PDL asignará y destruirá espacios de trabajo grandes pero temporales para operaciones intermedias. En ese caso, es posible que desee considerar el uso de Inline::Pdlpp
. Sin embargo, esa no es una herramienta para principiantes, así que no saltes hasta que hayas determinado que es lo mejor para ti.
Otra alternativa a todo esto es el uso de Inline::C
así:
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]++;
}
}
Para mí, la función en línea gana a ambos Perl y PDL. Para valores más grandes de $updates_per_round
, digamos 1,000, obtengo la versión Inline::C
aproximadamente 5 veces más rápido que Perl puro y entre 1.2x y 2x más rápido que PDL. Incluso cuando $updates_per_round
es solo 1, donde Perl supera a PDL, el código en línea es 2.5 veces más rápido que el código Perl.
Si esto es todo lo que necesita para llevar a cabo, le recomiendo usar Inline::C
.
Pero si necesita realizar muchas manipulaciones a sus datos, lo mejor es seguir con PDL por su potencia, flexibilidad y rendimiento. Vea a continuación cómo puede usar vec()
con datos PDL.
¿Ha estudiado los módulos perl bioinformáticos? – AlfredoVR