Tuesday, October 6, 2009

Closure vs. Object in Perl

I was curios how closures performs in comparison with objects in Perl. I have tested it on counter as the simplest possible abstraction with state.

use Benchmark qw(cmpthese);

sub make_counter {
    my $counter = shift;
    return sub { $counter++ };
}

package counter;

sub new {
    my ( $class, $counter ) = @_;
    return bless \$counter, $class;
}

sub inc { ${ shift() }++ }

package main;

our $inc = make_counter(1);

our $counter = counter->new(1);

cmpthese(
    -5,
    {   closure_make => q{make_counter(1)},
        object_make  => q{counter->new(1)}
    }
);

cmpthese(
    -5,
    {   closure => q{$main::inc->()},
        method  => q{$main::counter->inc()}
    }
);

And results are:

                 Rate closure_make  object_make
closure_make 420045/s           --         -35%
object_make  643969/s          53%           --
             Rate  method closure
method  2397172/s      --    -35%
closure 3697681/s     54%      --

Well, it is not simple. Both approaches have their benefits but if you want best performance and want little bit abstraction you should pass closure into your hot loop instead objects. But if you want create closure inside loop, than let you try avoid it or change to object. But you should not need it in hot loop anyway. I'm little bit surprised that closure construction is so expensive in perl. I have known that bless is not cheap operation, but closure?

1 comment:

nothingmuch said...

Closures require capturing values from the lexical pad of in the callstack and creating a new CV object with those values.

Devel::Peek show all the that needs to get constructed.

This is about as much work as creating a new hash and allocating data in it, in terms of the number of memory allocations required, etc.

Personally I wouldn't go either way. If that small difference is really meaningful, then you shouldn't be using Perl (OCaml comes to mind). Use whatever is cleaner =)