[Dbix-class] Class::C3 and branches/DBIx-Class-C3

Matt S Trout dbix-class at trout.me.uk
Tue Nov 15 20:52:28 CET 2005


Ok, so as the benchmark below demonstrates Class::C3 runs *much* faster
than NEXT (as of the 0.05 dist, quicksilver and I optimised it a little).
It also should free us from a number of NEXT bugs.

I've created a branch to attempt to make a port; it's somewhat working but
there are still some unresolvable inheritance chains in there. Anybody
who's got commit to trunk should have commit to the branch, so if you can
tweak something and get more tests passing, commit it. Stuff it, it's a
branch. If we break it I'll make another one :)

##################################################################

perl main.pl
All tests are run 5000 times, basic task is adding up numbers 1..10

                     Basic function call     0.047 s      9.5 us/iter
               function calling function     0.052 s     10.3 us/iter
     anonymous function calling function     0.061 s     12.1 us/iter
                 method calling function     0.063 s     12.5 us/iter
  method calling function stored in self     0.066 s     13.1 us/iter
                         simple subclass     0.066 s     13.2 us/iter
                        SUPER redispatch     0.100 s     20.0 us/iter
                      SUPER^2 redispatch     0.141 s     28.2 us/iter
                      SUPER^3 redispatch     0.226 s     45.2 us/iter
                      SUPER^4 redispatch     0.192 s     38.4 us/iter
                         NEXT redispatch     0.702 s    140.3 us/iter
                       NEXT^2 redispatch     1.319 s    263.7 us/iter
                       NEXT^3 redispatch     1.976 s    395.2 us/iter
                       NEXT^4 redispatch     2.334 s    466.7 us/iter
                    NEXT, 4 Dummy supers     1.638 s    327.6 us/iter
                    NEXT, 8 Dummy supers     2.227 s    445.5 us/iter
              NEXT, 4 Passthrough supers     3.256 s    651.1 us/iter
      NEXT::ACTUAL, 4 Passthrough supers     3.231 s    646.3 us/iter
                    Class::C3 redispatch     0.379 s     75.8 us/iter
                  Class::C3^2 redispatch     0.315 s     63.0 us/iter
                  Class::C3^3 redispatch     0.433 s     86.6 us/iter
                  Class::C3^4 redispatch     0.605 s    121.0 us/iter
               Class::C3, 4 Dummy supers     0.487 s     97.4 us/iter
               Class::C3, 8 Dummy supers     0.523 s    104.6 us/iter

#####################################################

#!/usr/bin/perl

use Time::HiRes qw/sleep gettimeofday tv_interval/;

my $num_iters = 5000;
my $sum_to = 10;

print "All tests are run $num_iters times, ",
  "basic task is adding up numbers 1..$sum_to\n\n";

sub dosomething { my $tot; foreach my $i (1..$sum_to) {$tot+=$i} }
run_test(\&dosomething,"Basic function call");

sub something_else { dosomething(); }
run_test(\&something_else,"function calling function");

run_test(sub {dosomething();},"anonymous function calling function");

my @objtests = 
  (
   ObjTest1=>"method calling function",
   ObjTest2=>"method calling function stored in self",
   ObjTest3=>"simple subclass",
   ObjTestS1=>"SUPER redispatch",
   ObjTestS2=>"SUPER^2 redispatch",
   ObjTestS3=>"SUPER^3 redispatch",
   ObjTestS4=>"SUPER^4 redispatch",
   ObjTestN1=>"NEXT redispatch",
   ObjTestN2=>"NEXT^2 redispatch",
   ObjTestN3=>"NEXT^3 redispatch",
   ObjTestN4=>"NEXT^4 redispatch",
   ObjTestND4=>"NEXT, 4 Dummy supers",
   ObjTestND8=>"NEXT, 8 Dummy supers",
   ObjTestNPT4=>"NEXT, 4 Passthrough supers",
   ObjTestNPTA4=>"NEXT::ACTUAL, 4 Passthrough supers",
   ObjTestC31=>"Class::C3 redispatch",
   ObjTestC32=>"Class::C3^2 redispatch",
   ObjTestC33=>"Class::C3^3 redispatch",
   ObjTestC34=>"Class::C3^4 redispatch",
   ObjTestC3D4=>"Class::C3, 4 Dummy supers",
   ObjTestC3D8=>"Class::C3, 8 Dummy supers",
  );

my $i;
for ($i = 0;$i < $#objtests;$i+=2 ) {
  my ($k,$v) = @objtests[$i,$i+1];
  $obj = $k->new(\&dosomething);
  run_test(sub {$obj->doit},$v);
}

package ObjTest1;
sub new {my $class = shift; return bless {},$class}
sub doit { my $self = shift; ::dosomething(); }

package ObjTest2;
sub new {my $class = shift;my $sub = shift; 
	 return bless {sub=>$sub},$class}
sub doit { my $self = shift; $self->{sub}->(); }

package ObjTest3;
use base 'ObjTest2';

package ObjTestS1;
use base 'ObjTest2';
sub doit { my $self = shift; $self->SUPER::doit(@_); }

package ObjTestS2;
use base 'ObjTestS1';
sub doit { my $self = shift; $self->SUPER::doit(@_); }

package ObjTestS3;
use base 'ObjTestS2';
sub doit { my $self = shift; $self->SUPER::doit(@_); }

package ObjTestS4;
use base 'ObjTestS3';
sub doit { my $self = shift; $self->SUPER::doit(@_); }

package ObjTestN1;
use base 'ObjTest2';
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package ObjTestN2;
use base 'ObjTestN1';
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package ObjTestN3;
use base 'ObjTestN2';
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package ObjTestN4;
use base 'ObjTestN3';
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package Dummy1; sub blank{};
package Dummy2; sub blank{};
package Dummy3; sub blank{};
package Dummy4; sub blank{};

package ObjTestND4;
use base qw/Dummy1 Dummy2 Dummy3 Dummy4 ObjTestN1/;
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package Dummy5; sub blank{};
package Dummy6; sub blank{};
package Dummy7; sub blank{};
package Dummy8; sub blank{};

package ObjTestND8;
use base qw/Dummy5 Dummy6 Dummy7 Dummy8 ObjTestND4/;
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package PassThrough1; sub doit { my $self = shift; $self->NEXT::doit(@_); }
package PassThrough2; sub doit { my $self = shift; $self->NEXT::doit(@_); }
package PassThrough3; sub doit { my $self = shift; $self->NEXT::doit(@_); }
package PassThrough4; sub doit { my $self = shift; $self->NEXT::doit(@_); }

package ObjTestNPT4;
use base qw/PassThrough1 PassThrough2 PassThrough3 PassThrough4 ObjTestN1/;
use NEXT;
sub doit { my $self = shift; $self->NEXT::doit(@_); }

package PTA1; sub doit { my $self = shift; $self->NEXT::ACTUAL::doit(@_); }
package PTA2; sub doit { my $self = shift; $self->NEXT::ACTUAL::doit(@_); }
package PTA3; sub doit { my $self = shift; $self->NEXT::ACTUAL::doit(@_); }
package PTA4; sub doit { my $self = shift; $self->NEXT::ACTUAL::doit(@_); }

package ObjTestNPTA4;
use base qw/PTA1 PTA2 PTA3 PTA4 ObjTestN1/;
use NEXT;
sub doit { my $self = shift; $self->NEXT::ACTUAL::doit(@_); }

package ObjTestC31;
use base 'ObjTest2';
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package ObjTestC32;
use base 'ObjTestC31';
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package ObjTestC33;
use base 'ObjTestC32';
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package ObjTestC34;
use base 'ObjTestC33';
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package ObjTestC3D4;
use base qw/Dummy1 Dummy2 Dummy3 Dummy4 ObjTestC31/;
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package ObjTestC3D8;
use base qw/Dummy5 Dummy6 Dummy7 Dummy8 ObjTestC3D4/;
use Class::C3;
sub doit { my $self = shift; $self->next::method(@_); }

package main;
my $start;
sub timer_start { $start = [gettimeofday]; }
sub timer_stop { return tv_interval $start; }

sub run_test {
  my $sub = shift;
  my $name = shift;

  timer_start();
  for (1..$num_iters) {
    $sub->();
  }
  $time = timer_stop();
  printf "%40s    %6.3f s   %6.1f us/iter\n",
    $name,$time,($time/$num_iters)*1000000;
}

-- 
     Matt S Trout       Specialists in Perl consulting, web development, and
  Technical Director    UNIX/Linux systems architecture and automation. Mail
Shadowcat Systems Ltd.  mst (at) shadowcatsystems.co.uk for more information

 + Help us build a better perl ORM: http://dbix-class.shadowcatsystems.co.uk/ +



More information about the Dbix-class mailing list