2009-07-05 15 views
14

Varias veces he leído que unpack() es más rápido que substr(), especialmente a medida que aumenta el número de subcadenas. Sin embargo, este punto de referencia sugiere lo contrario. ¿Es defectuoso mi índice de referencia o la presunta ventaja de rendimiento de unpack() es un remanente de versiones anteriores de Perl?¿Desembalaje de Perl() es más rápido que substr()?

use strict; 
use warnings; 
use Benchmark; 

my ($data, $format_string, $n_substrings); 

my %methods = (
    unpack => sub { return unpack $format_string, $data }, 
    substr => sub { return map {substr $data, $_, 1} 0 .. $n_substrings - 1 }, 
); 

for my $exp (1 .. 5){ 
    $n_substrings = 10 ** $exp; 
    print $n_substrings, "\n"; 
    $format_string = 'a1' x $n_substrings; 
    $data   = 9 x $n_substrings; 
    Benchmark::cmpthese -2, \%methods; 
} 

de salida (en Windows):

10 
      Rate unpack substr 
unpack 131588/s  -- -52% 
substr 276802/s 110%  -- 
100 
      Rate unpack substr 
unpack 13660/s  -- -57% 
substr 31636/s 132%  -- 
1000 
     Rate unpack substr 
unpack 1027/s  -- -68% 
substr 3166/s 208%  -- 
10000 
     Rate unpack substr 
unpack 84.4/s  -- -74% 
substr 322/s 281%  -- 
100000 
     Rate unpack substr 
unpack 5.46/s  -- -82% 
substr 30.1/s 452%  -- 

Como se señala en algunas respuestas, unpack() le va mal en Windows. Aquí está la salida en una máquina Solaris - no tan decisiva, pero substr() todavía gana la carrera a pie:

10 
      Rate unpack substr 
unpack 202274/s  -- -4% 
substr 210818/s  4%  -- 
100 
      Rate unpack substr 
unpack 22015/s  -- -9% 
substr 24322/s 10%  -- 
1000 
     Rate unpack substr 
unpack 2259/s  -- -9% 
substr 2481/s 10%  -- 
10000 
     Rate unpack substr 
unpack 225/s  -- -9% 
substr 247/s  9%  -- 
100000 
     Rate unpack substr 
unpack 22.0/s  -- -10% 
substr 24.4/s 11%  -- 
+2

Nada es más rápido que cualquier otra cosa. Tienes que aplicarlo a un problema específico. ¿Es un guepardo más rápido que un ganso? Tal vez más de 100 metros, pero no al otro lado del océano. :) –

+3

@brian Tomo su punto, pero el comentario parece fuera de destino. Aquí hay un análogo más cercano: "¿Es un guepardo * siempre * más rápido que un ganso? ​​Aquí hay un ejemplo * específico de ellos compitiendo entre sí. ¿Mi prueba fue parcial?"? Respuesta: "Sí, parcial. El ganso estaba en esteroides". – FMc

Respuesta

3

Desde que realicé esta pregunta, he comparado varias veces substr con unpack, en diversas condiciones. Aquí hay algunas cosas que he aprendido:

  • no configura el punto de referencia de una manera que llama a las funciones de Perl en vacío contexto (como lo hice en mi pregunta original; ver la respuesta útil a partir dlowe). Algunas funciones de Perl tienen optimizaciones cuando se llaman en contexto vacío (y estas optimizaciones parecen variar según el sistema operativo), potencialmente sesgando los resultados de su evaluación comparativa.

  • Si el uso de substr implica bucle (por ejemplo, interactuando sobre una lista de ubicaciones de columna), unpack es siempre más rápido. Sin embargo, la aparente lentitud de substr en esta situación se debe a la sobrecarga de el bucle, no a substr.

  • Si se requieren sólo algunos campos, substr es generalmente más rápido o tan rápido como unpack.

  • Si hay más de un par de campos son requeridos, comparaciones de cabeza a cabeza entre unpack y un equivalente número de substr llamadas no varían tanto como el número de campos aumentos: ambos enfoques se convierten en más lenta a la misma velocidad.

  • Los resultados pueden variar según el sistema operativo. En mi máquina con Windows XP, unpack tenía una ligera ventaja cuando más de se necesitaban algunos campos. En nuestras máquinas Solaris en mi lugar de trabajo, substr siempre fue más rápido, incluso en cientos de campos.

En pocas palabras: el rendimiento de unpack vs substr no es un problema muy grande, sin importar el número de campos. Utilice el enfoque que resulte en el código más claro. Si se encuentra utilizando substr en una construcción de bucle, sin embargo, cambiar a unpack dará como resultado un aumento de velocidad notable.

2

no quiere decir que estoy desconfiando de sus resultados, pero ¿qué tipo de sistema que se ejecuta en este? Me encontré con la secuencia de comandos en Ubuntu 8.10 (Perl 5.10) con los siguientes resultados:

[email protected]:~$ perl -v 

This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi 
[email protected]:~$ ./test.pl 
10 
      Rate substr unpack 
substr 587390/s  -- -10% 
unpack 650343/s 11%  -- 
100 
      Rate substr unpack 
substr 66060/s  -- -5% 
unpack 69433/s  5%  -- 
1000 
     Rate substr unpack 
substr 6847/s  -- -2% 
unpack 6977/s  2%  -- 
10000 
     Rate substr unpack 
substr 683/s  -- -1% 
unpack 693/s  1%  -- 
100000 
     Rate substr unpack 
substr 68.3/s  -- -0% 
unpack 68.4/s  0%  -- 

mis resultados de mi máquina local de Windows (que es lo que supongo que está utilizando, a juzgar por los resultados de mi):

>perl -v 

This is perl, v5.10.0 built for MSWin32-x86-multi-thread 

>perl test.pl 
10 
      Rate unpack substr 
unpack 125210/s  -- -50% 
substr 252878/s 102%  -- 
100 
      Rate unpack substr 
unpack 12677/s  -- -56% 
substr 28854/s 128%  -- 
1000 
     Rate unpack substr 
unpack 963/s  -- -66% 
substr 2846/s 196%  -- 
10000 
     Rate unpack substr 
unpack 78.8/s  -- -73% 
substr 291/s 269%  -- 
100000 
     Rate unpack substr 
unpack 4.88/s  -- -82% 
substr 27.2/s 457%  -- 

Si tuviera que adivinar la diferencia, me atrevería a decir que Windows no tiene una función nativa pack/unpack, por lo que Perl tiene que emularlo de alguna manera. Mi 2c de todos modos.

+0

Excelente punto. Agregué un poco más de salida. – FMc

+0

resultados en una MacBook (OSX) son similares a los resultados de Ubuntu 8.10 ... – Massa

+0

Obtengo resultados similares en mi Ubuntu 8.10. –

4

puedo obtener resultados similares a la pregunta bajo Ubuntu 9:

This is perl, v5.10.0 built for i486-linux-gnu-thread-multi 
10 
     Rate unpack substr 
unpack 535925/s  -- -3% 
substr 552749/s  3%  -- 
100 
     Rate unpack substr 
unpack 57957/s  -- -5% 
substr 61264/s  6%  -- 
1000 
    Rate unpack substr 
unpack 4716/s  -- -22% 
substr 6075/s 29%  -- 
10000 
    Rate unpack substr 
unpack 466/s  -- -24% 
substr 609/s 31%  -- 
100000 
    Rate unpack substr 
unpack 46.3/s  -- -23% 
substr 60.5/s 31%  -- 

pero no estoy seguro de que esto es relevante. No tiendo a usar descomprimir para extracciones simples de cadenas, debido a su cadena de formato profano :-)

Creo que donde brillaría extraer enteros codificados y todo tipo de otra información binaria que es donde lo usaría .

Una cosa que debe tomar de Matthew's y mi (y su) puntos de referencia es que dependerá mucho de los factores ambientales. Y tenga en cuenta que la velocidad, aunque es buena, no es el todo-y-todo-no creo que haya escrito mucho código que se vería seriamente afectado por solo poder realizar 4,6 millones de extracciones por segundo en lugar de 6 millones :-) Usted puede necesita ese rendimiento extra, pero lo dudo para la mayoría de las aplicaciones escritas en Perl.

+0

+1 en general, pero especialmente sobre la cadena de formato de desempaquetado ... En especial, estoy de acuerdo con usted acerca de la idea de que pack/unpack esté diseñado para datos binarios en lugar de simplemente tomar subseries. –

6

La prueba no es defectuosa, pero está sesgada. substr es mejor si todo lo que necesitas hacer es extraer una subcadena bastante simple de una cadena, pero eso es todo. Por ejemplo, incluso esta simple tarea no se hace fácilmente usando substr:

$foo = '123foo456789bar89012'; 
my ($t1,$t2,$t3,$t4,$t5) = unpack("A3A3A6A3A5",$foo); 

Aquí es donde usted debe ver una diferencia dramática entre substr y desempacar.

19

Como cuestión de hecho, su punto de referencia es defectuosa, de una manera muy, muy interesante, pero lo que se reduce a que es lo que realmente está comparando es la eficiencia relativa con la que desempaquetar vs mapa puede tirar una lista, porque Benchmark :: cmpthese() está ejecutando las funciones en el contexto vacío.

La razón por la que su substr aparece en la parte superior es esta línea de código en pp_ctl.c pp_mapwhile():

if (items && gimme != G_VOID) { 

es decir, un mapa de Perl se salta mágicamente un grupo de trabajo (es decir, la asignación de almacenamiento de los resultados del mapa) si se sabe que está siendo llamado en el contexto vacío!

(Mi corazonada en las ventanas frente a otras que se ven arriba es que la asignación de memoria perl basada en Windows es horrible, por lo que omitir la asignación es un ahorro mayor allí - solo una corazonada, sin embargo, no tengo una ventana caja para jugar. Pero la implementación real de desempaquetar es código C directo, y no debe diferir sustancialmente de Windows a otra.)

Tengo tres soluciones diferentes para solucionar este problema y generar una comparación más justa:

  1. asignar la lista a una matriz
  2. bucle sobre la lista dentro de la función, y devolver nada
  3. devolver una referencia a la lista (ocultando el contexto vacío)

Aquí está mi versión de los métodos%, con las tres versiones:

my %methods = (
    unpack_assign => sub { my @foo = unpack $format_string, $data; return }, 
    unpack_loop => sub { for my $foo (unpack $format_string, $data) { } }, 
    unpack_return_ref => sub { return [ unpack $format_string, $data ] }, 
    unpack_return_array => sub { return unpack $format_string, $data }, 

    substr_assign => sub { my @foo = map {substr $data, $_, 1} 0 .. ($n_substrings - 1) }, 
    substr_loop => sub { for my $foo (map {substr $data, $_, 1} 0 .. ($n_substrings - 1)) { } }, 
    substr_return_ref => sub { return [ map {substr $data, $_, 1} 0 .. ($n_substrings - 1) ] }, 
    substr_return_array => sub { return map { substr $data, $_, 1} 0 .. ($n_substrings - 1) }, 
); 

Y mis resultados :

$ perl -v 

This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi 

$ perl foo.pl 
10 
         Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array 
substr_assign  101915/s   --    -20%  -21%   -28%    -51%  -51%    -65%    -69% 
substr_return_ref 127224/s   25%    --   -1%   -10%    -39%  -39%    -57%    -62% 
substr_loop   128484/s   26%    1%   --   -9%    -38%  -39%    -56%    -61% 
unpack_assign  141499/s   39%    11%   10%   --    -32%  -32%    -52%    -57% 
unpack_return_ref 207144/s   103%    63%   61%   46%    --   -1%    -29%    -37% 
unpack_loop   209520/s   106%    65%   63%   48%    1%   --    -28%    -37% 
unpack_return_array 292713/s   187%    130%  128%   107%    41%   40%     --    -12% 
substr_return_array 330827/s   225%    160%  157%   134%    60%   58%     13%     -- 
100 
         Rate substr_assign substr_loop substr_return_ref unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array 
substr_assign  11818/s   --  -25%    -25%   -26%    -53%  -55%    -63%    -70% 
substr_loop   15677/s   33%   --    -0%   -2%    -38%  -40%    -51%    -60% 
substr_return_ref 15752/s   33%   0%    --   -2%    -37%  -40%    -51%    -60% 
unpack_assign  16061/s   36%   2%    2%   --    -36%  -39%    -50%    -59% 
unpack_return_ref 25121/s   113%   60%    59%   56%    --   -4%    -22%    -35% 
unpack_loop   26188/s   122%   67%    66%   63%    4%   --    -19%    -33% 
unpack_return_array 32310/s   173%  106%    105%   101%    29%   23%     --    -17% 
substr_return_array 38910/s   229%  148%    147%   142%    55%   49%     20%     -- 
1000 
         Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array 
substr_assign  1309/s   --    -23%  -25%   -28%    -52%  -54%    -62%    -67% 
substr_return_ref 1709/s   31%    --   -3%   -6%    -38%  -41%    -51%    -57% 
substr_loop   1756/s   34%    3%   --   -3%    -36%  -39%    -49%    -56% 
unpack_assign  1815/s   39%    6%   3%   --    -34%  -37%    -48%    -55% 
unpack_return_ref 2738/s   109%    60%   56%   51%    --   -5%    -21%    -32% 
unpack_loop   2873/s   120%    68%   64%   58%    5%   --    -17%    -28% 
unpack_return_array 3470/s   165%    103%   98%   91%    27%   21%     --    -14% 
substr_return_array 4015/s   207%    135%  129%   121%    47%   40%     16%     -- 
10000 
        Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array 
substr_assign  131/s   --    -23%  -27%   -28%    -52%  -55%    -63%    -67% 
substr_return_ref 171/s   30%    --   -5%   -6%    -38%  -42%    -52%    -57% 
substr_loop   179/s   37%    5%   --   -1%    -35%  -39%    -50%    -55% 
unpack_assign  181/s   38%    6%   1%   --    -34%  -38%    -49%    -55% 
unpack_return_ref 274/s   109%    60%   53%   51%    --   -6%    -23%    -32% 
unpack_loop   293/s   123%    71%   63%   62%    7%   --    -18%    -27% 
unpack_return_array 356/s   171%    108%   98%   96%    30%   21%     --    -11% 
substr_return_array 400/s   205%    134%  123%   121%    46%   37%     13%     -- 
100000 
         Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array 
substr_assign  13.0/s   --    -22%  -26%   -29%    -51%  -55%    -63%    -67% 
substr_return_ref 16.7/s   29%    --   -5%   -8%    -37%  -43%    -52%    -58% 
substr_loop   17.6/s   36%    5%   --   -3%    -33%  -40%    -50%    -56% 
unpack_assign  18.2/s   40%    9%   3%   --    -31%  -37%    -48%    -54% 
unpack_return_ref 26.4/s   103%    58%   50%   45%    --   -9%    -25%    -34% 
unpack_loop   29.1/s   124%    74%   65%   60%    10%   --    -17%    -27% 
unpack_return_array 35.1/s   170%    110%   99%   93%    33%   20%     --    -12% 
substr_return_array 39.7/s   206%    137%  125%   118%    50%   36%     13%     -- 

Volviendo a la pregunta original: "es unpack() cada vez más rápido que substr()?" Respuesta: siempre, para este tipo de aplicación, a menos que no le importen los valores devueltos;)

+1

¡Gran respuesta, gracias! – FMc

+2

¡Fue divertido! & de nada :) – dlowe

+1

Esta es casi la misma situación que discuto en el capítulo de Benchmarking en Mastering Perl. En contexto vacío, grep es realmente rápido. –

Cuestiones relacionadas