Pergunta sobre perl – Perl é descompactado () cada vez mais rápido que substr ()?

14

Várias vezes eu li issounpack() é mais rápido quesubstr(), especialmente à medida que o número de substrings aumenta. No entanto, este benchmark sugere o contrário. Meu benchmark é falho, ou é a alegada vantagem de desempenho deunpack() um resquício de versões antigas do 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;
}

Saída (no 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 apontado em algumas respostas,unpack() faz mal no Windows. Aqui está a saída em uma máquina solaris - não tão decisiva, massubstr() ainda vence a corrida a pé:

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 eu tomo seu ponto, mas o comentário parece fora do alvo. Aqui está um análogo mais próximo: "É uma chitasempre mais rápido que um ganso? Aqui está umespecífico exemplo deles competindo entre si. Meu teste foi tendencioso? "? Resposta:" Sim, tendencioso. O ganso estava em esteróides ". FMc
Nada é mais rápido que qualquer coisa. Você tem que aplicá-lo para um problema específico. É uma chita mais rápida que um ganso? Talvez mais de 100 metros, mas não através de um oceano. :) brian d foy

Sua resposta

5   a resposta
4

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%     --

Mas não tenho certeza se isso é relevante. Eu não costumo usar descompactar para extrações de strings simples, devido à sua string de formato profana :-)

Eu acho que onde iria brilhar é extrair inteiros codificados e todos os tipos de outras informações binárias que é onde eu iria usá-lo.

Uma coisa que você deve tirar do Matthew e do meu (e seu) benchmarks é que isso dependerá muito de fatores ambientais. E tenha em mente que a velocidade, embora seja boa, não é o melhor e o fim de tudo - não creio que tenha escrito muito código que seria seriamente afetado por apenas ser capaz de realizar 4,6 milhões de extrações por segundo, em vez de 6 milhões :-) Vocêmaio Preciso desse desempenho extra, mas duvido para a maioria dos aplicativos escritos em Perl.

+1 em geral, mas especialmente sobre unpacks format string ... Eu concordo especialmente com você sobre a idéia de que pack / unpack é projetado para dados binários ao invés de simplesmente usar substrings. Matthew Scharley
19

é falho, de uma maneira realmente interessante, mas o que se resume a isso é que o que você está realmente comparando é a eficiência relativa com a qual o unpack vs.jogue fora uma lista, porque Benchmark :: cmpthese () está executando as funções no contexto vazio.

A razão pela qual seu substr é a parte superior dessa linha de código está em pp_ctl.c pp_mapwhile ():

if (items && gimme != G_VOID) {

Ou seja, o mapa do perl pula magicamente um monte de trabalho (ou seja, alocar armazenamento para os resultados do mapa) se ele sabe que está sendo chamado no contexto vazio!

(Meu palpite nas janelas versus outro visto acima é que a alocação de memória perl baseada em janelas é horrível, então pular a alocação é uma economia maior lá - só um palpite, no entanto, eu não tenho uma caixa de janelas para brincar Mas a implementação real do desempacotamento é um código C direto e não deve diferir substancialmente das janelas para outras.

Eu tenho três soluções diferentes para contornar esse problema e gerar uma comparação mais justa:

atribuir a lista a um arrayfaça um loop sobre a lista dentro da função e não retorne nadaretornar uma referência à lista (ocultando o contexto vazio)

Aqui está a minha versão dos métodos%, com todas as três versões:

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

E meus 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%                  --

Então, voltando à pergunta original: "é unpack () sempre mais rápido que substr ()?" Resposta: sempre, para este tipo de aplicação - a menos que você não se importe com os valores de retorno;)

Ótima resposta - obrigado! FMc
Esta é quase a mesma situação que eu discuto no capítulo de Benchmarking em Mastering Perl. No contexto vazio, o grep é realmente veloz! brian d foy
Foi divertido! & seja bem-vindo :) dlowe
3

substr contraunpack várias vezes mais, sob várias condições. Aqui estão algumas coisas que aprendi:

Não configure o benchmark de uma maneira que chame as funções Perl no contexto vazio (como fiz na minha pergunta original; veja a resposta útil do dlowe). Algumas funções Perl têm otimizações quando são chamadas no contexto void (e essas otimizações parecem variar por sistema operacional), potencialmente distorcendo seus resultados de benchmarking.

Se o seu uso desubstr envolve loop (por exemplo, iterando sobre uma lista de locais de coluna),unpack é sempre mais rápido. No entanto, a aparente lentidãosubstr nesta situação é devido à sobrecarga do circuito, nãosubstr em si.

Se apenas alguns campos forem necessários,substr é geralmente mais rápido ou tão rápido quantounpack.

Se forem necessários mais do que alguns campos, comparações frente-a-frente entreunpack e um número equivalente desubstr as chamadas não variam muito conforme o número de campos aumenta: ambas as abordagens se tornam mais lentas na mesma proporção.

Os resultados podem variar de acordo com o sistema operacional. Na minha máquina com Windows XP,unpack tinha uma ligeira vantagem sempre que mais do que alguns campos eram necessários. Em nossas máquinas Solaris no meu local de trabalho,substr foi sempre mais rápido, mesmo em centenas de campos.

Linha de fundo: o desempenho deunpack vs.substr não é um problema muito grande, independentemente do número de campos. Use a abordagem que resultar no código mais claro. Se você se encontrar usandosubstr em uma construção de looping, no entanto, a mudança paraunpack resultará em um aumento de velocidade notável.

6

mas é distorcido. substr é melhor se tudo que você precisa fazer é extrair uma substring bastante simples de uma string, mas é sobre isso. Por exemplo, mesmo essa tarefa simples não é feita facilmente usando substr:

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

É aí que você deve ver uma diferença dramática entre substr e descompactar.

2

mas em que tipo de sistema você está executando isso? Eu corri o seu script no Ubuntu 8.10 (perl 5.10) com os seguintes 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%     --

Meus resultados da minha máquina Windows local (que é o que eu estou supondo que você está usando, a julgar pelos meus 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%     --

Se eu tivesse que adivinhar a diferença, eu teria um palpite e diria que o Windows não tem uma função nativa de empacotar / descompactar e então o Perl tem que imitá-lo de alguma forma. Meu 2c de qualquer maneira.

Ponto excelente. Eu adicionei mais alguns resultados. FMc
Sim ... Estou disposto a apostar que o baixo desempenho no Windows é devido a uma implementação nativa deficiente (ou falta de) de empacotar / descompactar ... Matthew Scharley
resultados em um MacBook (OSX) são semelhantes aos resultados do seu Ubuntu 8.10 ... Massa
Eu recebo resultados semelhantes no meu Ubuntu 8.10. Brad Gilbert

Perguntas relacionadas