Skip Menu |

This queue is for tickets about the Class-MOP CPAN distribution.

Report information
The Basics
Id: 42513
Status: resolved
Priority: 0/
Queue: Class-MOP

People
Owner: Nobody in particular
Requestors: gfuji [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Performance improvement
Hello, Because method modifiers are really useful, I have written its XS implementation in Data::Util, which is faster than the current implementation (v0.75), especially in "before" and "after" modifiers. Moreover, even if no C compiler is available, Data::Util works currectly because it has also a Pure Perl impementation. I have attached a patch using Data::Util, requiring v0.43. All the tests in Class::MOP(v0.75) and Moose(v0.64) are passed successfully. Regards. -- Goro Fuji (gfx) GFUJI at CPAN.org
Subject: Wrapped.diff
--- lib/Class/MOP/Method/Wrapped.pm.orig 2009-01-01 01:39:14.000000000 +0900 +++ lib/Class/MOP/Method/Wrapped.pm 2009-01-19 16:09:27.268457000 +0900 @@ -5,7 +5,7 @@ use warnings; use Carp 'confess'; -use Scalar::Util 'blessed'; +use Data::Util; our $VERSION = '0.75'; $VERSION = eval $VERSION; @@ -13,148 +13,57 @@ use base 'Class::MOP::Method'; -# NOTE: -# this ugly beast is the result of trying -# to micro optimize this as much as possible -# while not completely loosing maintainability. -# At this point it's "fast enough", after all -# you can't get something for nothing :) -my $_build_wrapped_method = sub { - my $modifier_table = shift; - my ($before, $after, $around) = ( - $modifier_table->{before}, - $modifier_table->{after}, - $modifier_table->{around}, - ); - if (@$before && @$after) { - $modifier_table->{cache} = sub { - $_->(@_) for @{$before}; - my @rval; - ((defined wantarray) ? - ((wantarray) ? - (@rval = $around->{cache}->(@_)) - : - ($rval[0] = $around->{cache}->(@_))) - : - $around->{cache}->(@_)); - $_->(@_) for @{$after}; - return unless defined wantarray; - return wantarray ? @rval : $rval[0]; - } - } - elsif (@$before && !@$after) { - $modifier_table->{cache} = sub { - $_->(@_) for @{$before}; - return $around->{cache}->(@_); - } - } - elsif (@$after && !@$before) { - $modifier_table->{cache} = sub { - my @rval; - ((defined wantarray) ? - ((wantarray) ? - (@rval = $around->{cache}->(@_)) - : - ($rval[0] = $around->{cache}->(@_))) - : - $around->{cache}->(@_)); - $_->(@_) for @{$after}; - return unless defined wantarray; - return wantarray ? @rval : $rval[0]; - } - } - else { - $modifier_table->{cache} = $around->{cache}; - } -}; - sub wrap { my ( $class, $code, %params ) = @_; - (blessed($code) && $code->isa('Class::MOP::Method')) + Data::Util::is_instance($code, 'Class::MOP::Method') || confess "Can only wrap blessed CODE"; - my $modifier_table = { - cache => undef, - orig => $code, - before => [], - after => [], - around => { - cache => $code->body, - methods => [], - }, - }; - $_build_wrapped_method->($modifier_table); my $method = $class->SUPER::wrap( - sub { $modifier_table->{cache}->(@_) }, - # get these from the original - # unless explicitly overriden + Data::Util::modify_subroutine($code->body), package_name => $params{package_name} || $code->package_name, name => $params{name} || $code->name, ); - $method->{'modifier_table'} = $modifier_table; + $method->{original} = $code; $method; } sub get_original_method { my $code = shift; - $code->{'modifier_table'}->{orig}; + $code->{original}; } sub add_before_modifier { my $code = shift; my $modifier = shift; - unshift @{$code->{'modifier_table'}->{before}} => $modifier; - $_build_wrapped_method->($code->{'modifier_table'}); + Data::Util::subroutine_modifier($code->body, before => $modifier); } sub before_modifiers { my $code = shift; - return @{$code->{'modifier_table'}->{before}}; + Data::Util::subroutine_modifier($code->body, 'before'); } sub add_after_modifier { my $code = shift; my $modifier = shift; - push @{$code->{'modifier_table'}->{after}} => $modifier; - $_build_wrapped_method->($code->{'modifier_table'}); + Data::Util::subroutine_modifier($code->body, after => $modifier); } sub after_modifiers { my $code = shift; - return @{$code->{'modifier_table'}->{after}}; + Data::Util::subroutine_modifier($code->body, 'after'); } -{ - # NOTE: - # this is another possible candidate for - # optimization as well. There is an overhead - # associated with the currying that, if - # eliminated might make around modifiers - # more manageable. - my $compile_around_method = sub {{ - my $f1 = pop; - return $f1 unless @_; - my $f2 = pop; - push @_, sub { $f2->( $f1, @_ ) }; - redo; - }}; - - sub add_around_modifier { +sub add_around_modifier { my $code = shift; my $modifier = shift; - unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier; - $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->( - @{$code->{'modifier_table'}->{around}->{methods}}, - $code->{'modifier_table'}->{orig}->body - ); - $_build_wrapped_method->($code->{'modifier_table'}); - } + Data::Util::subroutine_modifier($code->body, around => $modifier); } sub around_modifiers { my $code = shift; - return @{$code->{'modifier_table'}->{around}->{methods}}; + Data::Util::subroutine_modifier($code->body, 'around'); } 1;
Subject: Re: [rt.cpan.org #42513] Performance improvement
Date: Mon, 19 Jan 2009 09:22:24 +0200
To: bug-Class-MOP [...] rt.cpan.org
From: Yuval Kogman <nuffin [...] cpan.org>
Maybe this functionality of Data::Util should be merged into Class::MethodModifiers and Class::MOP can depend on that instead of having a separate implementation? That way Mouse also benefits Also, what about tracing these routines with the debugger? I think it should probably be fairly clear what's going on if the modifiers are properly named with Sub::Name, but if they are anonymous it could be confusing since the XS portion of the runtime won't show up in the call stack or trace. There should probably be an environment variable to disable the XS acceleration (Class::MOP already has one, fwiw). Cheers, Yuval
On Sun Jan 18 23:23:39 2009, NUFFIN wrote: Show quoted text
> Also, what about tracing these routines with the debugger? I think it > should probably be fairly clear what's going on if the modifiers are > properly named with Sub::Name, but if they are anonymous it could be > confusing since the XS portion of the runtime won't show up in the > call stack or trace. There should probably be an environment variable > to disable the XS acceleration (Class::MOP already has one, fwiw).
There is an environment variable to use the PurePerl implementation: DATA_UTIL_PUREPERL. Thanks, -- Goro Fuji (gfx) GFUJI at CPAN.org
Remove from wishlist. -- Goro Fuji (gfx) GFUJI at CPAN.org