<?xml version="1.0" encoding="windows-1252"?>
<node id="338521" title="Lies, Damn Lies and Benchmarks" created="2004-03-21 17:27:31" updated="2005-08-14 17:28:04">
<type id="120">
perlmeditation</type>
<author id="272239">
liz</author>
<data>
<field name="doctext">
Sometimes you feel like you need a slap to the head.
&lt;P&gt;
Yesterday was such a day when I finally figured out that a strange phenomenon, which has been discussed at one Perl Mongers meeting, as well as in a BOF on a Perl Workshop, is actually caused by a very bad benchmark.  Lies and Damn Lies, indeed.
&lt;P&gt;
The phenomenon was that a benchmark showed that it was faster to do:
&lt;code&gt;
my $self = shift;
my %param = @_;
&lt;/code&gt;
rather than:
&lt;code&gt;
my ($self,%param) = @_;
&lt;/code&gt;
This seems counter-intuitive, but the benchmark&lt;sup&gt;*&lt;/sup&gt; was repeatable and showed that using a shift and a seperate assignment of the hash was almost three times as fast as doing in one list assignment.  Just to make sure, I ran the benchmark with both 5.8.3 and 5.6.2.  The benchmark and the result:
&lt;code&gt;
use Benchmark qw(cmpthese);
cmpthese( -2,{
 list =&gt; sub { my ($self,%param) = @_ },
 shiftit =&gt; sub { my $self = shift; my %param = @_ },
} );
__END__
5.8.3       Rate    list shiftit
list     78392/s      --    -73%
shiftit 292680/s    273%      --
5.6.2       Rate    list shiftit
list     94936/s      --    -68%
shiftit 297717/s    214%      --
&lt;/code&gt;
&lt;P&gt;
While preparing a some articles about micro-optimizations for Perl Monks, I decided to test this some more.  Because I suddenly realised that I was testing this without parameters actually being passed.  So I figured I'd do a run with parameters actually being passed.  And &lt;B&gt;everything changed&lt;/B&gt;.  Observe:
&lt;code&gt;
sub list { my ($self,%param) = @_ }
sub shiftit { my $self = shift; my %param = @_ }

cmpthese( -2,{
 list =&gt; sub { list( qw(foo bar baz) ) },
 shiftit =&gt; sub { shiftit( qw(foo bar baz) ) },
} );
__END__
5.8.3      Rate shiftit    list
shiftit 70621/s      --    -11%
list    79125/s     12%      --
5.6.2      Rate shiftit    list
shiftit 89341/s      --    -10%
list    98866/s     11%      --
&lt;/code&gt;
Huh?  What's this?  So doing it in one list assignment apparently is more efficient if you pass enough parameters to the subroutine.  Hmmm...  but what if we don't have parameters for the hash assignment.  Surely then it would be faster to use the approach using &lt;code&gt;shift()&lt;/code&gt;?
Nope.
&lt;code&gt;
cmpthese( -2,{
 list =&gt; sub { list( qw(foo) ) },
 shiftit =&gt; sub { shiftit( qw(foo) ) },
} );
__END__
5.8.3       Rate shiftit    list
shiftit 164545/s      --    -19%
list    204158/s     24%      --
5.6.2       Rate shiftit    list
shiftit 179408/s      --    -12%
list    203990/s     14%      --
&lt;/code&gt;
The list assignment was &lt;B&gt;still&lt;/B&gt; more efficient.  Huh?  Had I been testing wrong.  Ok, surely without any parameters passed, it would be faster to use &lt;code&gt;shift()&lt;/code&gt;?  Again, nope!
&lt;code&gt;
cmpthese( -2,{
 list =&gt; sub { list() },
 shiftit =&gt; sub { shiftit() },
} );
__END__
5.8.3       Rate shiftit    list
shiftit 217350/s      --    -12%
list    247334/s     14%      --
5.6.2       Rate shiftit    list
shiftit 212031/s      --    -14%
list    246447/s     16%      --
&lt;/code&gt;
So what was the difference between my original benchmark and this one, apart from the overhead of calling an extra subroutine for each iteration?  What was I missing?
&lt;P&gt;
&lt;readmore&gt;
The thing I was missing was in this little piece of documentation in [perldoc://perlsub]:
&lt;code&gt;
To call subroutines:

           NAME(LIST);    # &amp; is optional with parentheses.
           NAME LIST;     # Parentheses optional if predeclared/imported.
           &amp;NAME(LIST);   # Circumvent prototypes.
           &amp;NAME;         # Makes current @_ visible to called subroutine.
&lt;/code&gt;
and indeed, if I changed the call from &lt;code&gt;foo()&lt;/code&gt; to &lt;code&gt;&amp;foo&lt;/code&gt;, the following benchmark came about:
&lt;code&gt;
cmpthese( -2,{
 list =&gt; sub { &amp;list },
 shiftit =&gt; sub { &amp;shiftit },
} );
__END__
5.8.3       Rate    list shiftit
list    126315/s      --    -62%
shiftit 333577/s    164%      --
5.6.2       Rate    list shiftit
list    135814/s      --    -61%
shiftit 343986/s    153%      --
&lt;/code&gt;
And indeed, that's a &lt;B&gt;lot&lt;/B&gt; closer to the original benchmark.
&lt;P&gt;
What further conclusions can be drawn from this?  Not sure, I guess I'll leave that as an excercise to the reader.  ;-)
&lt;/readmore&gt;
&lt;P&gt;
This just goes to show that you should always check, doublecheck and triplecheck your benchmarks.
&lt;P&gt;
Liz
&lt;P&gt;
&lt;sup&gt;*&lt;/sup&gt;Please note that benchmarks can be off by 5 to 10% between runs.  I've run the each benchmark multiple times, but some of them were run while running on battery power, and others were run when my iBook was plugged in.  Within one benchmark, I always had the situation consistent, so the results between 5.6.2 and 5.8.3 of a benchmark &lt;B&gt;can&lt;/B&gt; be compared (keeping in mind the 5 - 10% uncertainty for each run, of course).</field>
</data>
</node>
