Pregunta sobre perl – ¿El desempaque () de Perl es más rápido que substr ()?

14

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

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;
}

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() hace mal en Windows. Aquí está la salida en una máquina solaris - no tan decisiva, perosubstr() sigue ganando 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%     --
@brian Tomo tu punto, pero el comentario parece fuera de lugar. Aquí hay un análogo más cercano: "Es un guepardosiempre más rápido que un ganso? Aquí está unespecífico Ejemplo de ellos compitiendo entre sí. ¿Mi prueba fue parcial? "? Respuesta:" Sí, parcial. El ganso estaba en los esteroides ". FMc
Nada es más rápido que cualquier cosa. Tienes que aplicarlo a problema específico. ¿Es un guepardo más rápido que un ganso? Tal vez más de 100 metros, pero no a través de un océano. :) brian d foy

Tu respuesta

5   la respuesta
2

No quiere decir que desconfíe de tus resultados, pero ¿en qué tipo de sistema estás ejecutando esto? Ejecuté su script 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 Windows local (que es lo que supongo que estás usando, a juzgar por mis resultados):

>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, haría una suposición descabellada y diría que Windows no tiene una función de empaquetar / desempaquetar nativa, por lo que Perl debe emularla de alguna manera. Mi 2c de todos modos.

Obtengo resultados similares en mi Ubuntu 8.10. Brad Gilbert
Excelente punto. Agregué un poco más de salida. FMc
Sí ... estoy dispuesto a apostar que el bajo rendimiento en Windows se debe a una implementación pobre (o falta de) de empaquetar / desempaquetar ... Matthew Scharley
los resultados en una MacBook (OSX) son similares a los resultados de Ubuntu 8.10 ... Massa
4

Obtengo resultados similares al interrogador en 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 sea relevante. No suelo desempaquetar para extracciones de cadena simples, debido a su cadena de formato no profético :-)

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

Una cosa que debe tomar de los puntos de referencia de Matthew y my (y sus) es que dependerá mucho de los factores ambientales. Y tenga en cuenta que la velocidad, si bien es buena, no es el principio y el fin de todo. No creo que haya escrito mucho código que se vería seriamente afectado solo por poder realizar 4.6 millones de extracciones por segundo en lugar de 6 millones :-) tumayo Necesito ese rendimiento adicional, pero lo dudo para la mayoría de las aplicaciones escritas en Perl.

+1 en general, pero especialmente sobre desempaquetar cadenas de formato ... Estoy especialmente de acuerdo con usted sobre la idea de que empacar / desempaquetar está diseñado para datos binarios en lugar de simplemente tomar subcadenas. Matthew Scharley
19

De hecho, su punto de referencia.es defectuoso, de una manera muy, muy interesante, pero todo se reduce a eso es que lo que realmente estás comparando es la eficiencia relativa con la que se puede desempaquetar vs. mapatirar una lista, porque Benchmark :: cmpthese () está ejecutando las funciones en un contexto vacío.

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

if (items && gimme != G_VOID) {

es decir, el mapa de Perl omite un montón de trabajo (es decir, asignando almacenamiento para los resultados del mapa) si sabe que se está llamando en un contexto vacío.

(Mi corazonada en las ventanas frente a otras vistas anteriormente es que la asignación de memoria Perl basada en Windows es horrible, por lo que saltarse la asignación es un ahorro más grande allí. Sin embargo, no tengo una caja de ventanas para jugar. . Pero la implementación real de desempaquetar es un código C directo, y no debería diferir sustancialmente de Windows a otro.)

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

asignar la lista a una matrizrecorre la lista dentro de la función y no devuelve nadadevolver una referencia a la lista (ocultando el contexto vacío)

Aquí está mi versión de% methods, 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%                  --

Así que 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 se preocupe por los valores de retorno;)

Esta es casi la misma situación que discuto en el capítulo de Benchmarking en Mastering Perl. En el contexto vacío, grep es realmente rápido! brian d foy
¡Fue divertido! & De nada :) dlowe
Gran respuesta - gracias! FMc
3

Desde que hice esta pregunta, me he referidosubstr en contraunpack Varias veces más, bajo diversas condiciones. Aquí hay algunas cosas que he aprendido:

No configure el punto de referencia de una manera que llame a las funciones de Perl en un contexto vacío (como lo hice en mi pregunta original; vea la respuesta útil de dlowe). Algunas funciones de Perl tienen optimizaciones cuando se las llama en un contexto nulo (y estas optimizaciones parecen variar según el sistema operativo), lo que podría distorsionar los resultados de la evaluación comparativa.

Si su uso desubstr implica bucles (por ejemplo, iterar sobre una lista de ubicaciones de columnas),unpack siempre es mas rapido Sin embargo, la aparente lentitud desubstr en esta situación se debe a la sobrecarga del bucle, nosubstr sí mismo.

Si solo se requieren algunos campos,substr es generalmente más rápido o tan rápido comounpack.

Si se requieren más de unos pocos campos, comparaciones directas entreunpack y un número equivalente desubstr Las llamadas no varían mucho a medida que aumenta el número de campos: ambos enfoques se vuelven más lentos al mismo ritmo.

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

Línea de fondo: El rendimiento deunpack contrasubstr no es un problema muy grande, independientemente del número de campos. Use el enfoque que resulte en el código más claro. Si te encuentras usandosubstr en una construcción en bucle, sin embargo, cambiando aunpack resultará en un aumento de velocidad notable.

6

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

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

Ahí es donde debería ver una diferencia dramática entre substr y desempaquetar.

Preguntas relacionadas