Is there anything wrong with this benchmark?

Class::Method::Modifiers versus $self->SUPER.

The exec summary is:

$ dumbbench perl test1.pl 
cmd: Ran 21 iterations (1 outliers).
cmd: Rounded run time per iteration: 2.0408e-02 +/- 3.6e-05 (0.2%)
$ dumbbench perl test2.pl 
cmd: Ran 22 iterations (2 outliers).
cmd: Rounded run time per iteration: 1.5050e-02 +/- 6.9e-05 (0.5%)

So Class::Method::Modifiers seems 30% faster. I'm guessing that this is the result of not having the subroutine overhead call on every invocation. Am I right? How does this work?

Here's the code:

  • test1.pl

    package One;
    sub blah {
        my ($self) = @_;
        return 1;
    }
    
    
    package Two;
    use base qw/One/;
    
    
    sub blah {
        my ($self) = @_;
        my $orig = $self->SUPER::blah(@_);
        return ++$orig;
    }
    
    
    package main;
    for (1 .. 10000) {
        Two->blah;
    }
    
  • test2.pl

    use warnings;
    use strict;
    
    
    package One;
    sub blah {
        my ($self) = @_;
        return 1;
    }
    
    
    package Two;
    use base qw/One/;
    use Class::Method::Modifiers;
    
    
    around  blah => sub  {
        my ($orig) = shift;
        my ($self) = @_;
        my $ret = $orig->($self, @_);
        return ++$ret;
    };
    
    
    package main;
    for (1 .. 10000) {
        Two->blah;
    }
    

3 Comments

My guess is that the main saving is in not having to do a lookup to find the superclass. Perl has to do this at run time for each call because @ISA can be changed at any time (even though it's unusual to change it once you've initially set it).

I believe Toby is right, and the sub created by around will not recognize any later changes to the inheritance (or redefining of the original sub), which obviously makes it more efficient - as long as you are not making such changes.

With ->SUPER, your sub is being called first and then it looks up what the superclass is via inheritance. With around, it *creates a new sub* in place and then passes you (at call time) a reference to the previously existing sub, or the method that would have been inherited at the time of 'around'. You're right that later changes to inheritance would not be recognized (changing inheritance at runtime seems like a bad idea in general) but redefining the sub itself would override the 'around' completely since it already installed into that name, so that wouldn't be useful either.

Leave a comment

About kd

user-pic Australian perl hacker. Lead author of the Definitive Guide to Catalyst. Dabbles in javascript, social science and statistical analysis. Seems to have been sucked into the world of cloud and devops.