2012-06-11 62 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,并不在乎它的表现。