2009-06-15 14 views
8

He estado trabajando en varios scripts de Perl que procesan grandes archivos de datos de ancho fijo, extrayendo pequeñas subcadenas de cada registro de datos. Había imaginado que delegar la extracción de subcadenas en llamadas a métodos sería costoso debido a la sobrecarga de copiar el registro de datos en la matriz @_. Entonces ejecuté lo siguiente para comparar (a) la llamada directa a substr(), (b) la llamada al método que pasa el registro de datos como una cadena, y (c) la llamada al método que pasa el registro de datos por referencia.¿Tiene un golpe de rendimiento de copia de datos al pasar argumentos a las subrutinas de Perl?

use strict; 
use warnings; 
use Benchmark qw(timethese); 

my $RECORD = '0' x 50000; 

my $direct = sub { my $v = substr($RECORD, $_, 1) for 0..999 }; 
my $byVal = sub { my $v = ByVal ($RECORD, $_) for 0..999 }; 
my $byRef = sub { my $v = ByRef (\$RECORD, $_) for 0..999 }; 

sub ByVal { return substr( $_[0], $_[1], 1) } 
sub ByRef { return substr(${$_[0]}, $_[1], 1) } 

timethese(10000, { 
    direct => $direct, 
    byVal  => $byVal, 
    byRef  => $byRef, 
}); 

my $byVal2loc = sub { my $v = ByVal2loc($RECORD, $_) for 0..999 }; 
my $byRef2loc = sub { my $v = ByRef2loc(\$RECORD, $_) for 0..999 }; 

sub ByVal2loc { my $arg = shift; return substr( $arg, $_[0], 1) } 
sub ByRef2loc { my $arg = shift; return substr($$arg, $_[0], 1) } 

timethese($ARGV[0], { 
    byVal2loc => $byVal2loc, 
    byRef2loc => $byRef2loc, 
}); 

# Produces this output: 
Benchmark: timing 10000 iterations of byRef, byVal, direct... 
    byRef: 19 wallclock secs... 
    byVal: 15 wallclock secs... 
    direct: 4 wallclock secs... 

Benchmark: timing 10000 iterations of byRef2loc, byVal2loc... 
byRef2loc: 21 wallclock secs... 
byVal2loc: 119 wallclock secs... 

Como era de esperar, el método directo fue el más rápido. Sin embargo, me sorprendió no encontrar ninguna penalización relacionada con la "copia de datos" que había estado imaginando. Incluso cuando aumenté el ancho del registro a proporciones estrafalarias (por ejemplo, mil millones de caracteres), los puntos de referencia de los valores por referencia y por referencia fueron básicamente los mismos.

Parece que al pasar argumentos a los métodos, Perl no copia los datos. Supongo que esto tiene sentido al reflexionar sobre el poder de alias de @_. Los argumentos se pasan por referencia, no por valor.

Sin embargo, es una forma limitada de paso de referencia por referencia, porque las referencias en @_ no se pueden asignar directamente a una variable local dentro de la subrutina. Tales asignaciones resultan en la copia de datos, como se ilustra en el segundo conjunto de puntos de referencia.

¿Lo entiendo bien?

Respuesta

8

Sí, tareas de copia; solo pasar argumentos no. Sin embargo, puede alias léxicos a elementos en @_ usando Lexical::Alias. Este punto de referencia modificado muestra haciendo que un tercio más rápido que el uso de una referencia, pero constantemente por lo que independientemente de la longitud de $ EXPEDIENTE:

use strict; 
use warnings; 
use Benchmark qw(timethese); 
use Lexical::Alias; 

my $RECORD = '0' x 5000000; 

my $byVal2loc = sub { my $v = ByVal2loc($RECORD, $_) for 0..999 }; 
my $byRef2loc = sub { my $v = ByRef2loc(\$RECORD, $_) for 0..999 }; 
my $byAlias2loc = sub { my $v = ByAlias2loc($RECORD, $_) for 0..999 }; 

sub ByVal2loc { my $arg = shift; return substr( $arg, $_[0], 1) } 
sub ByRef2loc { my $arg = shift; return substr($$arg, $_[0], 1) } 
sub ByAlias2loc { my $arg; alias($_[0], $arg); return substr($arg, $_[0], 1 ) } 

timethese($ARGV[0], { 
    byVal2loc => $byVal2loc, 
    byRef2loc => $byRef2loc, 
    byAlias2loc => $byAlias2loc, 
}); 

# output: 
Benchmark: running byAlias2loc, byRef2loc, byVal2loc for at least 3 CPU seconds... 
byAlias2loc: 3 wallclock secs (3.16 usr + 0.00 sys = 3.16 CPU) @ 430.70/s (n=1361) 
byRef2loc: 4 wallclock secs (3.24 usr + 0.00 sys = 3.24 CPU) @ 1329.63/s (n=4308) 
byVal2loc: 5 wallclock secs (4.95 usr + 0.01 sys = 4.96 CPU) @ 0.40/s (n=2) 
      (warning: too few iterations for a reliable count) 

(. Directamente utilizando alias_r lugar de la función de alias ayudante es marginalmente más rápido)

6

IIRC, en un 'sub' Perl, la matriz @_ ya es un conjunto de alias (referencias) a las variables. Si modifica $_[0], afecta la variable en la función de llamada.

#!/bin/perl -w 
use strict; 

sub x 
{ 
    print "x = $_[0]\n"; 
    $_[0] = "pinkerton"; 
    print "x = $_[0]\n"; 
} 

my $y = "abc"; 

print "y = $y\n"; 
x($y); 
print "y = $y\n"; 

La salida es:

y = abc 
x = abc 
x = pinkerton 
y = pinkerton 
+0

1. Esta es la respuesta correcta. –

+0

@Igor Krivokon: Correcto, sí, pero ya mencionado en la pregunta, al menos implícitamente. Supongo que "Sí, estás entendiendo esto correctamente"."le falta algo como respuesta. – ysth

0

Si desea dar a los elementos de @_ nombres significativos, puede hacer alias a ellos utilizando Data::Alias, por lo

use Data::Alias; 

sub foo { 
    alias my ($a, $b, $c) = @_; 
} 

se pueden hacer cosas similares aliasing en arrays y hashes.

alias my ($a, $b, @c) = @_; 
    alias my ($a, $b, %c) = @_; 

De hecho, aliasing en un hash

alias my (%p) = @_; 

es especialmente potente, ya que proporciona el paso por referencia parámetros con nombre. Bonito.

(Alias ​​de datos :: proporciona un superconjunto de la funcionalidad del léxico :: Alias, es más de uso general y más potente.)

Cuestiones relacionadas