簡體   English   中英

Perl的unpack()是否比substr()更快?

[英]Is Perl's unpack() ever faster than substr()?

有幾次我讀過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()在Windows上表現不佳。 這是solaris機器上的輸出 - 不是那么具有決定性,但是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%     --

事實上,你的基准有缺陷的,真的,非常有趣的方式,但它歸結為你真正比較的是解包與地圖可以扔掉一個列表的相對效率,因為Benchmark :: cmpthese()正在執行void上下文中的函數。

你的substr出現在頂部的原因是pp_ctl.c pp_mapwhile()中的這行代碼:

if (items && gimme != G_VOID) {

即perl的地圖神奇地跳過一堆工作(即為地圖的結果分配存儲),如果它知道它是在虛擬上下文中被調用的話!

(我對Windows的預感與上面提到的其他內容相比,基於Windows的perl內存分配很糟糕,所以跳過分配是一個更大的節省 - 只是預感,但是,我沒有一個Windows框可以玩但實際的解壓縮實現是直接的C代碼,並且不應該與其他窗口大不相同。)

我有三種不同的解決方案來解決這個問題並產生更公平的比較:

  1. 將列表分配給數組
  2. 循環遍歷函數內的列表,並且不返回任何內容
  3. 返回對列表的引用(隱藏void上下文)

這是我的%方法版本,包含所有三個版本:

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()更快嗎?” 答:總是,對於這種類型的應用 - 除非你不關心返回值;)

測試沒有缺陷,但它是偏斜的。 如果您需要做的只是從字符串中提取一個相當簡單的子字符串,那么substr會更好,但那就是它。 例如,使用substr即使這個簡單的任務也不容易完成:

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

那就是你應該看到substr和unpack之間的巨大差異。

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

但我不確定這是否相關。 我不傾向於使用unpack進行簡單的字符串提取,因為它的字符串格式不正確:-)

我認為它會閃耀的是提取編碼整數和各種其他二進制信息,這是我將使用它。

你應該從馬修和我的(以及你的)基准測試中得到的一點是,它將在很大程度上取決於環境因素。 請記住,速度雖然好,但不是最重要的 - 最終 - 我不認為我編寫的代碼會受到嚴重影響,因為每秒只能執行460萬次提取而不是600萬:-)您可能需要額外的性能,但我懷疑它是用Perl編寫的大多數應用程序。

自從提出這個問題以來,我已經在各種條件下對substr打了幾次unpack 以下是我學到的一些內容:

  • 不要以在void上下文中調用Perl函數的方式設置基准(正如我在原始問題中所做的那樣;請參閱dlowe的有用響應)。 一些Perl函數在void上下文中調用時會有優化(並且這些優化似乎因操作系統而異),可能會使基准測試結果出現偏差。

  • 如果您對substr的使用涉及循環(例如,迭代列位置列表),則unpack總是更快。 但是,在這種情況下, substr的明顯緩慢是由於循環的開銷,而不是substr本身。

  • 如果只需要幾個字段,則substr通常比unpack更快或更快。

  • 如果需要多個字段,則unpack和相同數量的substr調用之間的頭對頭比較隨着字段數量的增加而變化不大:兩種方法在相同速率下變慢。

  • 結果可能因操作系統而異。 在我的Windows XP計算機上,只要需要多個字段, unpack就會略有優勢。 在我工作場所的Solaris機器上, substr總是更快,甚至在數百個領域。

結論 :無論字段數量多少, unpacksubstr的性能都不是一個很大的問題。 使用最清晰的代碼中的任何一種方法。 但是,如果您發現自己在循環結構中使用substr ,切換到unpack將導致值得注意的速度提升。

不是說我不相信你的結果,但你運行這個系統是什么類型的系統? 我在Ubuntu 8.10(perl 5.10)上運行了你的腳本,結果如​​下:

 mscharley@S04:~$ perl -v

This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi
 mscharley@S04:~$ ./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沒有本機打包/解包功能,因此Perl必須以某種方式模擬它。 反正我的2c。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM