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?

Sunday, February 15, 2009

How fast or slow is Moose?

Moose is a postmodern object system for Perl 5 that takes the tedium out of writing object-oriented Perl. It borrows all the best features from Perl 6, CLOS (LISP), Smalltalk, Java, BETA, OCaml, Ruby and more, while still keeping true to its Perl 5 roots. It is very powerful and I was curious how fast is in current state. I have read that Moose is slow but all articles what I have found is about two years old. For me is important mostly runtime speed but compile time. So my benchmark is focused only to runtime. I also hate getter/setter combined accessors thus my tests are also only about separated getter and setter. Benchmark code follows.

#!/usr/bin/env perl

use strict;
use warnings;
use Data::Dumper;
use Benchmark qw(:all :hireswallclock);

{

 package MooseClassMutable;
 use Moose;

 has var => (
  is       => 'ro',
  reader   => 'get_var',
  writer   => 'set_var',
  required => 1
 );

}
{

 package MooseClassImmutable;
 use Moose;

 has var => (
  is       => 'ro',
  reader   => 'get_var',
  writer   => 'set_var',
  required => 1
 );

 no Moose;
 __PACKAGE__->meta->make_immutable;
}
{

 package PerlClass;

 sub new {
  my ( $class, %args ) = @_;
  die 'var value must be set' unless exists $args{var};
  return bless \%args, $class;
 }

 sub get_var { shift()->{var} }

 sub set_var { $_[0]->{var} = $_[1] }
}

{

 package MooseClassFast;
 use Moose;
 with 'MooseX::Emulate::Class::Accessor::Fast';

 has var => (
  is       => 'ro',
  required => 1
 );

 __PACKAGE__->follow_best_practice;
 __PACKAGE__->mk_accessors('var');

 no Moose;
 __PACKAGE__->meta->make_immutable;
}

cmpthese(
 -5,
 {   map {
   my $class = $_;
   "new $class" => sub { $class->new( var => 1 ) for 1 .. 1000 }
   } qw(MooseClassMutable MooseClassImmutable PerlClass MooseClassFast)
 }
);
my %objs = ( map { $_ => $_->new( var => 1 ) }
  qw(MooseClassMutable MooseClassImmutable PerlClass MooseClassFast) );
cmpthese(
 -5,
 {   map {
   my $class = $_;
   my $obj   = $objs{$class};
   "get $class" => sub { $obj->get_var() for 1 .. 1000 }
   } qw(MooseClassMutable MooseClassImmutable PerlClass MooseClassFast)
 }
);
cmpthese(
 -5,
 {   map {
   my $class = $_;
   my $obj   = $objs{$class};
   "set $class" => sub { $obj->set_var(1) for 1 .. 1000 }
   } qw(MooseClassMutable MooseClassImmutable PerlClass MooseClassFast)
 }
);

I have tested Moose versions 0.54 and 0.68 and just for curiosity also Class::Accessor::Fast emulation which works only with Moose version 0.68. Notice that rate is measured in thousands. Moose 0.54 results comes first.

                          Rate new MooseClassMutable new MooseClassImmutable new PerlClass
new MooseClassMutable   6.85/s                    --                    -96%          -98%
new MooseClassImmutable  192/s                 2697%                      --          -53%
new PerlClass            403/s                 5790%                    111%            --
                          Rate get MooseClassMutable get MooseClassImmutable get PerlClass
get MooseClassMutable   1716/s                    --                     -2%          -25%
get MooseClassImmutable 1754/s                    2%                      --          -23%
get PerlClass           2273/s                   32%                     30%            --
                          Rate set MooseClassImmutable set MooseClassMutable set PerlClass
set MooseClassImmutable 1611/s                      --                   -2%          -16%
set MooseClassMutable   1643/s                      2%                    --          -14%
set PerlClass           1916/s                     19%                   17%            --

Moose version 0.68 follows.

                          Rate new MooseClassMutable new MooseClassFast new MooseClassImmutable new PerlClass
new MooseClassMutable   15.4/s                    --               -84%                    -92%          -96%
new MooseClassFast      98.7/s                  541%                 --                    -48%          -76%
new MooseClassImmutable  190/s                 1138%                93%                      --          -54%
new PerlClass            412/s                 2579%               318%                    116%            --
                          Rate get MooseClassFast get MooseClassImmutable get MooseClassMutable get PerlClass
get MooseClassFast      1716/s                 --                     -2%                   -2%          -24%
get MooseClassImmutable 1743/s                 2%                      --                   -1%          -23%
get MooseClassMutable   1754/s                 2%                      1%                    --          -22%
get PerlClass           2261/s                32%                     30%                   29%            --
                          Rate set MooseClassFast set MooseClassMutable set MooseClassImmutable set PerlClass
set MooseClassFast      78.6/s                 --                  -95%                    -95%          -96%
set MooseClassMutable   1659/s              2011%                    --                     -1%          -15%
set MooseClassImmutable 1680/s              2038%                    1%                      --          -14%
set PerlClass           1950/s              2381%                   18%                     16%            --

Moose seems fast enough for me. If I realize how powerful Moose is results are great. I can persist class definition using make_immutable in most of cases and 190 thousand object constructions per second is enough. There is also big improvement in mutable version between 0.54 and 0.68 and 15 thousand per second is not terrible. Moose accessors are really fast and make_immutable have not any impact here. 1.7 million reads and 1.6 million writes per second is enough and my ugly handcrafted accessors can't make big difference here (2.2Mr/s and 1.9Mw/s). There is strange Class::Accessor:Fast setter result and I'm curious why. Anyway Moose itself performs well and there is not reason using it.

Wednesday, January 28, 2009

Absolute path resolver

I was looked for some utility to resolve absolute path of file or directory. I found namei but it result returns in form
$ namei /etc/gdc
f: /etc/gdc
d /
d etc
l gdc -> /home/hynek/.gdc
d /
d home
d hynek
l .gdc -> work/tiger/bear/common/etc/gdc
  d work
  d tiger
  l bear -> bear.trunk/
    d bear.trunk
  d common
  d etc
  d gdc
but I suspected something more like
$ abs_path /etc/gdc
/home/hynek/work/tiger/bear.trunk/common/etc/gdc
I haven't found anything better than make alias in my ~/.bashrc
alias abs_path='perl -MCwd -le'\''print Cwd::abs_path($_) foreach @ARGV'\'

Edit: As ZD notice readlink -f does same work. Thanks. readlink -m and readlink -e works as well but differs if some parts missing. I have decided to use

alias abs_path='readlink -m'

Wednesday, January 14, 2009

Erlang history summary by J. Armstrong

Joe Armstrong posts nice summary of Erlang history.

The transition was easy - they paid to do this. It became a real shipping project when they decided to use Erlang for the AXD301 - at that stage they put in the necessary $$$'s.

Now why did they choose Erlang for this project? - because all other alternatives had failed - ie it was not the strength of Erlang that was the deciding factor - rather the non-existence of alternatives.

Now how come the Erlang stuff was developed in the first place?

This was a happy accident - In the early 1980's a computer science lab was formed - most of the guys in the newly formed lab had zero experience with technology transfer, so we all thought that all we had to do was "invert stuff" and then "sell the idea to the management" nobody told us that this was like permanently banging your hand against a brick wall.

Inventing stuff is the easy bit ...

The selling stuff was tricky - we were very bad at this but very optimistic (still am :-) - we made all the classic mistakes - insulting people - getting into technical wars -

The turning point came when Erlang was banned - at the time we were very pissed off but like most carefull considered management decsions the net result was the exact opposite of what was planned - the consequences of the ban were difficult to forsee - but chaos was created - so things changed rapidly.

Thinking back the *important* things were:

  • enthusiasm and optimism (believe in what you do)
  • serendipity
  • chaos
  • smart people
  • finance

I think we systematically under-rate the significance of chance and chaos. Most significant change takes place in very short time periods of chaos. Erlang had many periods when nothing happened for years then rapid changes could take place in very short time periods, always when a crisis occurred (ie Erlang was banned, a big project failed etc).

Moral - forget about careful planning and move quickly when a crisis occurs - trust your gut feelings.

Cheers

/Joe Armstrong

Tuesday, January 13, 2009

History of Erlang

Joe Armstrong wrote nice article about Erlang History. It's worth reading whole article but I found most funny and nice part it's end.

6.4 Finally

It is perhaps interesting to note that the two most significant factors that led to the spread of Erlang were:

  • The collapse of the AXE-N project.
  • The Erlang ban.

Both of these factors were outside our control and were unplanned. These factors were far more significant than all the things we did plan for and were within our control. We were fortuitously able to take advantage of the collapse of the AXE-N project by rushing in when the project failed. That we were able to do so was more a matter of luck than planning. Had the collapse occurred at a different site then this would not have happened. We were able to step in only because the collapse of the project happened in the building where we worked so we knew all about it. Eventually Ericsson did the right thing (using the right technology for the job) for the wrong reasons (competing technologies failed). One day I hope they will do the right things for the right reasons.