Greg Heo

Benchmarking Perl

Featuring the Benchmark module from CPAN.

After browsing through the Perl documentation and reading perlperf, I had a sudden urge to profile some code. I’ve been guilty on many occasions of optimizing before profiling so think of this post as a kind of restitution.

Here’s a trivial piece of code I wrote long ago:

my $height = sprintf("%d'%d\"", $inches / 12, $inches % 12);

Given an inches value of 64, this will make a feet and height string like 5'4".

The sprintf() call is probably unnecessary and needlessly gives away my C background. I could “optimize” the line to this:

my $height = int($inches / 12) . "'" . $inches % 12 . '"';

That makes the same two calculations (one divide, one modulus) but eliminates the function call. It must be more efficient too, right?

Finally, here’s a snippet of code I found online that does the same thing in what I’m pretty certain is a horrible waste of cycles:

my $feet_dec = $inches / 12;
my @parts = split(/\./, $feet_dec);

my $feet = $parts[0];
my $fraction = int((('0.'.$parts[1])*12) + 0.5);

my $height = $feet."\'".$fraction."\"";

Wow. If that doesn’t inspire you to fire up the profiler, I don’t know what will.


Here’s the code to run things through the Benchmark module:

use Benchmark;

timethese(1000, {
  'sprintf' => sub 
    { for (my $i = 0; $i < 1000; $i++) { inches1($i); } },
  'calc'    => sub
    { for (my $i = 0; $i < 1000; $i++) { inches2($i); } },
  'long'    => sub
    { for (my $i = 0; $i < 1000; $i++) { inches3($i); } },

inches1, inches2, and inches3 contain the code snippets above. The results:

Benchmark: timing 1000 iterations of calc, long, sprintf...
      calc:  4 wallclock secs ( 3.69 usr +  0.03 sys =  3.72 CPU)
             @ 268.82/s (n=1000)
      long: 19 wallclock secs (16.89 usr +  0.13 sys = 17.02 CPU)
             @ 58.75/s (n=1000)
   sprintf:  4 wallclock secs ( 3.76 usr +  0.02 sys =  3.78 CPU)
             @ 264.55/s (n=1000)

For some reason, I thought the sprintf() call would be much more expensive than it really is. I can continue with my C coding habits after all!

What’s so horrible about the ‘long’ way of doing things? My guess is the call to split() that’s taking so much time. I sure wish there were some way to do a line-by-line analysis…