2012-06-11 60 views
6

爲什麼這裏的subeinselse慢於subzweielsif比elsif還慢嗎?

#!/usr/bin/env perl 
use warnings; 
use 5.012; 
use Benchmark qw(:all); 

my $d = 0; 
my $c = 2; 

sub eins { 
    if ($c == 1) { 
     $d = 1; 
    } 
    else { 
     $d = 2; 
    } 
} 

sub zwei { 
    if ($c == 1) { 
     $d = 1; 
    } 
    elsif ($c == 2) { 
     $d = 2; 
    } 
} 

sub drei { 
    $d = 1; 
    $d = 2 if $c == 2; 
} 

cmpthese(-5, { 
    eins => sub{ eins() }, 
    zwei => sub{ zwei() }, 
    drei => sub{ drei() }, 
}); 

 Rate eins drei zwei 
eins 4167007/s -- -1% -16% 
drei 4207631/s 1% -- -15% 
zwei 4972740/s 19% 18% -- 

     Rate eins drei zwei 
eins 4074356/s -- -8% -16% 
drei 4428649/s 9% -- -9% 
zwei 4854964/s 19% 10% -- 

     Rate eins drei zwei 
eins 3455697/s -- -6% -19% 
drei 3672628/s 6% -- -14% 
zwei 4250826/s 23% 16% -- 

     Rate eins drei zwei 
eins 2832634/s -- -8% -19% 
drei 3088931/s 9% -- -12% 
zwei 3503197/s 24% 13% -- 

     Rate eins zwei drei 
eins 3053821/s -- -17% -26% 
zwei 3701601/s 21% -- -10% 
drei 4131128/s 35% 12% -- 

     Rate eins drei zwei 
eins 3033041/s -- -2% -12% 
drei 3092511/s 2% -- -10% 
zwei 3430837/s 13% 11% -- 

Summary of my perl5 (revision 5 version 16 subversion 0) configuration: 

Platform: 
    osname=linux, osvers=3.1.10-1.9-desktop, archname=x86_64-linux 
    uname='linux linux1 3.1.10-1.9-desktop #1 smp preempt thu apr 5 18:48:38 utc 2012 (4a97ec8) x86_64 x86_64 x86_64 gnulinux ' 
    config_args='-de' 
    hint=recommended, useposix=true, d_sigaction=define 
    useithreads=undef, usemultiplicity=undef 
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef 
    use64bitint=define, use64bitall=define, uselongdouble=undef 
    usemymalloc=n, bincompat5005=undef 
Compiler: 
    cc='cc', ccflags ='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', 
    optimize='-O2', 
    cppflags='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include' 
    ccversion='', gccversion='4.6.2', gccosandvers='' 
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678 
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16 
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 
    alignbytes=8, prototype=define 
Linker and Libraries: 
    ld='cc', ldflags =' -fstack-protector -L/usr/local/lib' 
    libpth=/usr/local/lib /lib/../lib64 /usr/lib/../lib64 /lib /usr/lib /lib64 /usr/lib64 /usr/local/lib64 
    libs=-lnsl -lndbm -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat 
    perllibs=-lnsl -ldl -lm -lcrypt -lutil -lc 
    libc=/lib/libc-2.14.1.so, so=so, useshrplib=false, libperl=libperl.a 
    gnulibc_version='2.14.1'                                         
Dynamic Linking:                                           
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'                               
    cccdlflags='-fPIC', lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector'                           


Characteristics of this binary (from libperl): 
Compile-time options: HAS_TIMES PERLIO_LAYERS PERL_DONT_CREATE_GVSV 
         PERL_MALLOC_WRAP PERL_PRESERVE_IVUV USE_64_BIT_ALL 
         USE_64_BIT_INT USE_LARGE_FILES USE_LOCALE 
         USE_LOCALE_COLLATE USE_LOCALE_CTYPE 
         USE_LOCALE_NUMERIC USE_PERLIO USE_PERL_ATOF 
Built under linux 
Compiled at May 24 2012 20:53:15 
%ENV: 
    PERL_HTML_DISPLAY_COMMAND="/usr/bin/firefox -new-window %s" 
@INC: 
    /usr/local/lib/perl5/site_perl/5.16.0/x86_64-linux 
    /usr/local/lib/perl5/site_perl/5.16.0 
    /usr/local/lib/perl5/5.16.0/x86_64-linux 
    /usr/local/lib/perl5/5.16.0 
    . 
+0

我把這個作爲一個評論,因爲我不知道perl的膽量是如何工作的,但我猜想'zwei'的'elsif'構造可以更快找到,因爲它明確地綁定到'$ c'是'2','$ c'總是'2'。如果解釋者足夠聰明地發現'$ c'永遠不會改變,那可以解釋它。沒有那個,雖然你會期望'zwei'是較慢的,因爲它肯定需要測試'$ c'兩次。 –

+0

解釋器也可能把'zwei'條件變成一個跳轉表,就像C用'switch'所做的那樣。 – msw

+0

@Matthew Walton,'$ c'可以簡單地通過提取來改變,所以解釋器不能也不會做任何這樣的優化。 – ikegami

回答

3

你不得不檢查您的具體 Perl版本生成的操作碼。有趣的是,從調試器中運行時,該 結果是不同的:

Win64, Activeperl 5.14.2, from debugger environment: 
      Rate zwei eins drei 
zwei 130806/s -- -0% -1% 
eins 130957/s 0% -- -0% 
drei 131612/s 1% 1% -- 

沒有調試器,eins是最快的(正如人們從代碼預期):

Win64, Activeperl 5.14.2 : 
      Rate drei zwei eins 
drei 3402015/s -- -5% -13% 
zwei 3585171/s 5% -- -8% 
eins 3916856/s 15% 9% -- 

其實,我沒有在這裏找到一個系統,zwei快於eins

Linux x64, Perl 5.12.1: 
      Rate drei zwei eins 
drei 2439279/s -- -13% -15% 
zwei 2797316/s 15% -- -3% 
eins 2875184/s 18% 3% -- 


附錄

閱讀池上的發帖後,我試圖草莓5.16 Win64上,瞧:

Perl v5.16.0, MSWin32-x64-multi-t 
      Rate eins drei zwei 
eins 3954005/s -- -3% -10% 
drei 4084178/s 3% -- -7% 
zwei 4406707/s 11% 8% -- 

在這裏,我們走了,elsif/zwei更快

所以,這似乎是一個問題連接到5.16.0?

4

[這是每個說的答案,但它是有用的信息,不適合評論。 ]

首先讓我們看看編譯後的表格,如果$c == 2,「zwei」的執行路徑是「eins」的純超集。 (標有「*」)

*1 <0> enter       *1 <0> enter 
*2 <;> nextstate(main 4 -e:2) v:{  *2 <;> nextstate(main 4 -e:2) v:{ 
*3 <#> gvsv[*c] s      *3 <#> gvsv[*c] s 
*4 <$> const[IV 1] s     *4 <$> const[IV 1] s 
*5 <2> eq sK/2       *5 <2> eq sK/2 
*6 <|> cond_expr(other->7) vK/1   *6 <|> cond_expr(other->7) vK/1 
7  <0> enter v      7  <0> enter v 
8  <;> nextstate(main 1 -e:3) v:{ 8  <;> nextstate(main 1 -e:3) v:{ 
9  <$> const[IV 1] s     9  <$> const[IV 1] s 
a  <#> gvsv[*d] s     a  <#> gvsv[*d] s 
b  <2> sassign vKS/2     b  <2> sassign vKS/2 
c  <@> leave vKP      c  <@> leave vKP 
      goto d         goto d 
             *e <#> gvsv[*c] s 
             *f <$> const[IV 2] s 
             *g <2> eq sK/2 
             *h <|> and(other->i) vK/1 
*e <0> enter v       *i  <0> enter v 
*f <;> nextstate(main 2 -e:6) v:{  *j  <;> nextstate(main 2 -e:6) v:{ 
*g <$> const[IV 2] s     *k  <$> const[IV 2] s 
*h <#> gvsv[*d] s      *l  <#> gvsv[*d] s 
*i <2> sassign vKS/2     *m  <2> sassign vKS/2 
*j <@> leave vKP      *n  <@> leave vKP 
*d <@> leave[1 ref] vKP/REFC   *d <@> leave[1 ref] vKP/REFC 

事情是,我可以重現您的結果! (v5.16.0專爲x86_64的Linux的線程多)

  Rate drei eins zwei 
drei 8974033/s -- -3% -19% 
eins 9263260/s 3% -- -16% 
zwei 11034175/s 23% 19% -- 

      Rate drei eins zwei 
drei 8971868/s -- -1% -21% 
eins 9031677/s 1% -- -20% 
zwei 11333871/s 26% 25% -- 

這是一個不小的差異(這可能是CPU緩存的結果),它的不同運行之間reproduceable(所以它不是另一影響基準的應用程序)。我很難過。

每一次迭代中,它以22納秒(9031677分之1秒 - 11333871分之1S)做4個歡聲笑語。我預計它會花費大約100納秒。

+0

您確定這不能算入緩存或什麼嗎?也許你可以檢查'oprofile'來查看是否有任何區別(雖然我有點懷疑,但我通常沒有理解oprofile數據)。 – jpalecek

+0

@jpalecek,可以緩存未命中帳戶相當於一百個機器操作碼的損失? – ikegami

+0

是的,它可以。但是,我真的對這種行爲感到困惑 - 在任何合理的環境中,反覆運行一個小例程不應該產生很多緩存未命中,所以也許分支預測失誤?我不知道。 – jpalecek

0

我想最好的答案可能是:「誰在乎?」

這是最不成熟的微觀基準測試。如果分析發現真正的代碼緩慢的最大原因是使用else而不是elsif,那麼我有一個非常好的帽子,我會不願失去,但我會心跳吃掉。

就我個人而言,我會寫$d = ($c == 1) ? 1 : 2,並不在乎它的表現。