missingthepoint has asked for the wisdom of the Perl Monks concerning the following question:

Howdy.

Suppose I have an object which doesn't inherit, nor is it inherited from. Can I then say something like this:

my $foo = Foo->new; Foo::frequently_called_method($foo, @args);

... to speed things up? This implies 2 specific questions:

As a side note, is

my $foo = Foo->new; my $method = Foo->can('frequently_called_method'); $method->($foo, @args);
equivalent to the above? If so, it it faster or slower?

Thanks for your insight. :)


The zeroeth step in writing a module is to make sure that there isn't already a decent one in CPAN. (-- Pod::Simple::Subclassing)

Replies are listed 'Best First'.
Re: Can you speed up method calls by eliminating method dispatch?
by moritz (Cardinal) on Jul 08, 2009 at 12:23 UTC
    You can, but you severely limit extensibility. Remember that in general you don't know if anybody inherits from your classes.

    I don't know much about the speed implications, so I'd suggest you benchmark it. But I think that method lookup is cached, and that passing the arguments takes more time than the actual method dispatch.

      OK - I was thinking I had a case where it might be appropriate, but your first comment made me realize that while I might be OK with limiting my code's extensibility, I would be making things harder for other people who want to inherit from my object. I guess I wasn't thinking very socially :)

      I will indeed benchmark it... I didn't know method lookup is cached, but it wouldn't surprise me considering the years of optimizations Perl 5 has had (is that documented anywhere? I can find NOTE: can directly uses Perl's internal code for method lookup, and isa uses a very similar method and cache-ing strategy in perlobj).


      The zeroeth step in writing a module is to make sure that there isn't already a decent one in CPAN. (-- Pod::Simple::Subclassing)
Re: Can you speed up method calls by eliminating method dispatch?
by ikegami (Patriarch) on Jul 08, 2009 at 17:00 UTC
    Method lookups are cached so @ISA rarely needs to be traversed.
    $ perl -MDevel::Peek -e' { package Base; sub f {} } { package Child; @ISA="Base"; } Dump *Base::f; Dump *Child::f; Child->f(); Dump *Child::f; ' SV = PVGV(0x816c6e8) at 0x814f6d8 GP = 0x816c720 CV = 0x814f714 SV = PVGV(0x816f938) at 0x814f810 GP = 0x81709f8 CV = 0x0 <-- &Child::f doesn't exist CVGEN = 0x0 SV = PVGV(0x816f938) at 0x814f810 GP = 0x81709f8 CV = 0x814f714 <-- &Child::f aliased to &Base::f CVGEN = 0x4d <-- ...for method dispatch only

    (Irrelevant lines omitted)

      OK, thanks. I grok the aliasing, I think (some sort of NULL pointer changes to be a pointer to a CV, which new pointer holds the same address as Base::f's pointer?) With the Devel::Peek output, is this what's going on?

      (Looking at P5 source, the real magic behind Devel::Peek seems to be in Perl_do_sv_dump() in dump.c.)

      • 2 calls to Devel::Peek::Dump
      • Method call (dispatch)
      • 1 call to Devel::Peek::Dump

      Which translates to (first 2 calls):

      SV = PVGV(0x816c6e8) at 0x814f6d8 GP = 0x816c720 CV = 0x814f714 SV = PVGV(0x816f938) at 0x814f810 GP = 0x81709f8 CV = 0x0 <-- &Child::f doesn't exist CVGEN = 0x0

      (last):

      SV = PVGV(0x816f938) at 0x814f810 GP = 0x81709f8 CV = 0x814f714 <-- &Child::f aliased to &Base::f CVGEN = 0x4d <-- ...for method dispatch only

      Within the pared-down Devel::Peek output:

      SV = PVGV(0x816c6e8) at 0x814f6d8 # Reference (at 0x814f6d8) to typ +eglob (at 0x816c6e8)? GP = 0x816c720 # Address of typeglob? Has AV, HV +, CV. CV = 0x814f714 # Address of CV of f() SV = PVGV(0x816f938) at 0x814f810 # Akin to above GP = 0x81709f8 # "" CV = 0x0 # Child::f()'s pointer to CV, equ +iv place to Base::f() CVGEN = 0x0 # "generated CV"? do you mean "th +is CVGEN 'slot' is used for nothing else than method dispatch"?

      Please ignore beyond here, massive tangent :)
      No idea what 0x4d is, but within struct gp in gp.h, there is this:

      U32        gp_cvgen;    /* generational validity of cached gv_cv */

      ... and I can find stuff like this:

      gv.c 453: GvCVGEN(topgv) = PL_sub_generation;

      ... and PL_sub_generation seems to be incremented all over the place in the source. Furthermore:

      gv.c 457- else if (topgv && GvREFCNT(topgv) == 1) { 458- /* cache the fact that the method is not defined */ 459: GvCVGEN(topgv) = PL_sub_generation; ... 1433- /* Adding a new name to a subroutine invalidates method ca +che */ 1434: PL_sub_generation++;

      GvCVGEN is a macro that gets at the CVGEN in the Devel::Peek output:

      dump.c 1544: Perl_dump_indent(aTHX_ level, file, " CVGEN = 0x%"UVxf"\n", + (UV)GvCVGEN(sv));

      ... so line 3330 would relate to your "&Child::f doesn't exist" above:

      sv.c 3330- GvCVGEN(dstr) = 0; /* Switch off cacheness. */

      The zeroeth step in writing a module is to make sure that there isn't already a decent one in CPAN. (-- Pod::Simple::Subclassing)

        The point was that calling the method added the method to the child's namespace so that @ISA needs not be scanned on later calls to that method.

        Your questions go way beyond that, but I'll be happy to answer them anyway.

        I think (some sort of NULL pointer changes to be a pointer to a CV, which new pointer holds the same address as Base::f's pointer?)

        The CV field of the structure is the CODE slot of the glob. (See perldata)

        I was showing that *Child::f{CODE} was assigned *Base::f{CODE}. That means Base::f and Child::f are the same function at that point.

        do you mean "this CVGEN 'slot' is used for nothing else than method dispatch"?

        *Child::f{CODE} is effectively undefined for everything that isn't a method call when CVGEN is true.

        No idea what 0x4d is, but within struct gp in gp.h, there is [generational validity of cached gv_cv]

        The cached entries need to be expired when there's a change to an @ISA, but keeping track of all the cached entries would require a fair amount of resource.

        Instead of clearing all the cached entries (which would require keeping track of them), changing any @ISA simply increments a counter. If the cache's counter (CVGEN) is lower than the change counter, the cache is stale and thus ignored.

        Or so goes my understanding.

Re: Can you speed up method calls by eliminating method dispatch?
by Anonymous Monk on Jul 08, 2009 at 12:20 UTC
    There isn't appreciable speedup, but you can time it if you're curious. Also
    $foo->Foo::frequently_called_method(@args);