Вопрос по perl – Является ли Perl unpack () быстрее, чем substr ()?

14

Несколько раз я читал, чтоunpack() быстрее чемsubstr()Тем более что количество подстрок увеличивается. Тем не менее, этот тест предполагает иное. Является ли мой эталонный тест некорректным или предполагаемое преимущество в производительностиunpack() пережиток более старых версий 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;
}

Вывод (в 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%     --

Как указано в некоторых ответах,unpack() плохо работает на винде. Вот результат на машине соляриса - не такой уж решающий, ноsubstr() по-прежнему выигрывает гонки на ногах:

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%     --
@ Брайан, я понимаю твою точку зрения, но комментарий кажется не намеченным. Вот более близкий аналог: «Гепард»ever быстрее гуся? Вот этоspecific пример их гонок друг с другом. Был ли мой тест смещен? " Ответ: "Да, предвзятый. Гусь был на стероидах. FMc
Нет ничего быстрее, чем все. Вы должны применить это к конкретной проблеме. Гепард быстрее гуся? Может быть, более 100 метров, но не через океан. :) brian d foy

Ваш Ответ

5   ответов
4

Я получаю результаты, аналогичные тем, которые задают вопросы в 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%     --

Но я не уверен, что это актуально. Я не склонен использовать распаковку для простого извлечения строки из-за ее нечестивого формата строки :-)

Я думаю, где бы он ни сиял, извлекает закодированные целые числа и всякую другую двоичную информацию, где я бы ее использовал.

Одна вещь, которую вы должны принять из тестов Мэтью и моих (и ваших), состоит в том, что это будет во многом зависеть от факторов окружающей среды. И имейте в виду, что скорость, хотя и хорошая, не является основной и конечной - я не думаю, что я написал много кода, на который серьезно повлияла бы только возможность выполнять 4,6 миллиона операций извлечения в секунду, а не 6 миллионов :-) Выmay нужна дополнительная производительность, но я сомневаюсь в этом для большинства приложений, написанных на Perl.

+1 вообще, но особенно насчет формата строки распаковки ... Я особенно согласен с вами в том, что pack / unpack предназначен для двоичных данных, а не просто для взятия подстрок.
2

Не скажу, что я не доверяю вашим результатам, но на какой системе вы это используете? Я запустил ваш скрипт на Ubuntu 8.10 (perl 5.10) со следующими результатами:

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

Мои результаты с моего локального компьютера с Windows (именно это я и предполагаю, что вы используете, судя по моим результатам):

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

Если бы мне нужно было с уверенностью предположить разницу, я бы с уверенностью предположил и сказал, что в Windows нет встроенной функции pack / unpack, и поэтому Perl должен каким-то образом эмулировать ее. Мой 2с все равно.

Я получаю аналогичные результаты на моем Ubuntu 8.10.
результаты на MacBook (OSX) похожи на результаты Ubuntu 8.10 ...
Да ... Я готов поспорить, что низкая производительность на Windows связана с плохой (или отсутствующей) нативной реализацией упаковки / распаковки ...
Отличный момент. Я добавил еще вывод. FMc
3

С тех пор, как задал этот вопрос, я проверилsubstr противunpack еще несколько раз, при различных условиях. Вот несколько вещей, которые я изучил:

  • Do not set up the benchmark in a way that calls Perl functions in void context (as I did in my original question; see the helpful response from dlowe). Some Perl functions have optimizations when they are called in void context (and these optimizations appear to vary by OS), potentially skewing your benchmarking results.

  • If your use of substr involves looping (for example, iterating over a list of column locations), unpack is always faster. However, the apparent slowness of substr in this situation is due to the overhead of the loop, not substr itself.

  • If just a few fields are required, substr is generally faster or as fast as unpack.

  • If more than a few fields are required, head-to-head comparisons between unpack and an equivalent number of substr calls do not vary much as the number of fields increases: both approaches become slower at the same rate.

  • Results can vary by operating system. On my Windows XP machine, unpack had a slight edge whenever more than a few fields were needed. On our Solaris machines at my workplace, substr was always faster, even into hundreds of fields.

Bottom line: производительностьunpack противsubstr не очень большая проблема, независимо от количества полей. Используйте тот подход, который приведет к ясному коду. Если вы обнаружите, что используетеsubstr в циклической конструкции, однако, переключение наunpack приведет к заметному увеличению скорости.

19

На самом деле, ваш ориентирis ошибочным, действительно, действительно интересным способом, но все сводится к тому, что вы действительно сравниваете относительную эффективность, с которой распаковка против карты можетthrow away a listпотому что Benchmark :: cmpthese () выполняет функции в пустом контексте.

Причина, по которой ваш substr стоит выше, в том, что эта строка кода в pp_ctl.c pp_map while ():

if (items && gimme != G_VOID) {

карта perl волшебным образом пропускает кучу работы (а именно, выделяет хранилище для результатов карты), если знает, что она вызывается в пустом контексте!

(Мое предположение о том, что Windows отличается от других, показанных выше, заключается в том, что выделение памяти Perl на основе Windows ужасно, поэтому пропуск выделения ресурсов - это большая экономия - просто догадка, хотя у меня нет окна для игры с Но фактической реализацией распаковки является простой код C, и он не должен существенно отличаться от окон к другим.)

У меня есть три разных решения для решения этой проблемы и создания более справедливого сравнения:

  1. assign the list to an array
  2. loop over the list inside the function, and return nothing
  3. return a reference to the list (hiding the void context)

Вот моя версия методов% со всеми тремя версиями:

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

И мои результаты:

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

Итак, вернемся к первоначальному вопросу: «Является ли unpack () быстрее, чем substr ()?» Ответ: всегда, для этого типа приложения - если вы не заботитесь о возвращаемых значениях;)

Отличный ответ - спасибо! FMc
Это было весело! & Амп; добро пожаловать :)
Это почти та же самая ситуация, которую я обсуждаю в главе «Бенчмаркинг» в Mastering Perl. В пустом контексте grep действительно быстр!
6

Тест не ошибочен, но он искажен. substr лучше, если все, что вам нужно сделать, это извлечь довольно простую подстроку из строки, но это все. Например, даже эту простую задачу нелегко выполнить с помощью substr:

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

Вот где вы должны увидеть существенную разницу между substr и unpack.

Похожие вопросы