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

Greetings fellow monks,

do you have a clue about why

#!/usr/bin/perl -wl use strict; use Benchmark qw (cmpthese); my $ref = sub { @_ }; sub foo { @_ } cmpthese(-1, { refcall => sub { @_=(1); $ref -> (@_) }, named => sub { @_=(1); foo (@_) }, goto => sub { @_=(1); goto $ref }, } );

yields the following

Rate refcall named goto refcall 735179/s -- -1% -24% named 742317/s 1% -- -24% goto 973307/s 32% 31% --

unexpected results? I expected that

Reality, as often, seems to be the other way round.

--shmem

_($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                              /\_¯/(q    /
----------------------------  \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
first SoPW post

Replies are listed 'Best First'.
Re: named sub, subref and magic goto speed
by ysth (Canon) on Dec 14, 2006 at 07:31 UTC
    While goto $ref seems to be doing what you mean, it's technically a bug; goto has three forms, goto LABEL, goto EXPR, and goto &NAME. You should be saying goto &$ref, and goto $ref should be interpreted as the goto EXPR form (taking the stringification of $ref and using that as a label.)

      Oops! Good catch. I wonder how perl's not bailing out complaining e.g.

      Can't find label CODE(0x81678a0) at sub.pl line 13.

      and seems to be doing the right thing instead. The corrected code yields

      Rate refcall named goto refcall 708190/s -- -1% -10% named 717414/s 1% -- -9% goto 790676/s 12% 10% --
      but your previous reply (Re: named sub, subref and magic goto speed) explains that well: the goto &$ref essentially saves the building of one stack frame; that makes sense, thank you.

      <update>

      Uhm, no, goto &sub does create a new stack frame. From what I read in pp_ctl.c without knowing all those macros, I guess it's working like this:

      A new frame is created, the old frame is unwound (sort of return), the old frame inspected (whether inside a sub, an eval, a loop and so on), then @_ pushed onto the stack again and the new frame is entered (sort of call).

      It seems that the magic goto &function works along the semantics of

      my $ref = *function{'CODE'}; # get a reference goto $ref; # magic goto to that reference

      which explains why the goto &$coderef is more expensive - it does an extra deref and ref step:

      my $ref = \&{$coderef}; # get a reference (\&{&$ref} without +the call) goto $ref; # magic goto to that reference

      whereas whithout the & we have just

      goto $ref; # we have a code reference, just use +that

      Roughly. I mean, explaining what's going on in C speaking perl is sort of... well ;-)

      The relevant part of a diff of the output of two runs of the same code (foo.pl), once with goto $ref and once with goto &$ref (with -Dtls) shows what's going on:

      --- g_ref.Dtls +++ g_amp.Dtls @@ -69,7 +69,13 @@ => (foo.pl:5) nextstate => +(foo.pl:5) pushmark + => * (foo.pl:5) padsv($ref) + => * \CV(__ANON__) +(foo.pl:5) rv2cv + => * CV(__ANON__) +(foo.pl:5) refgen => \CV(__ANON__) (foo.pl:5) goto (foo.pl:5) (Found sub #1)

      <update>

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

        Oops! Good catch. I wonder how perl's not bailing out complaining

        Because it DWYM instead. The docs say a label is expected, but if a code ref is provided, goto $ref is treated as goto &$ref. Personally, I consider it a bug in the docs (not in Perl or your code) because there's no reason to treat goto $ref as it if were goto "$ref".

        $\ = "\n"; sub foo { print @_ } my $ref = \&foo; sub { @_=('$ref->(@_)'); $ref->(@_); }->(); sub { @_=('foo(@_)'); foo(@_); }->(); sub { @_=('goto $ref'); goto $ref; }->(); sub { @_=('goto &$ref'); goto &$ref; }->();
Re: named sub, subref and magic goto speed
by ysth (Canon) on Dec 14, 2006 at 07:28 UTC
    For refcall vs. named, you aren't seeing a significant difference.

    WRT the stack frame, refcall and named have to build a new stack frame and put the contents of @_ on the stack; that's heavy lifting.

      ysth,
      How does this differ from goto &sub;? I ask because of this.

      Cheers - L~R

        Apparently I was mistaken about the stack frame (unless it's changed since Larry wrote that). But the "heavy lifting" I meant was loading @_ onto the stack. For purposes of comparison:
        $ perl -MO=Concise,-exec,named,refcall,goto1,goto2 -we'my $ref = \&bar +; sub bar {} sub named { bar(@_) } sub refcall { $ref->(@_) } sub goto1 { goto &$re +f } sub goto2 { goto $ref }' main::named: 1 <;> nextstate(main 3 -e:2) v 2 <0> pushmark s 3 <$> gv(*_) s 4 <1> rv2av[t1] lKM/1 5 <$> gv(*bar) s 6 <1> entersub[t2] KS/TARG,1 7 <1> leavesub[1 ref] K/REFC,1 main::refcall: 8 <;> nextstate(main 4 -e:2) v 9 <0> pushmark s a <$> gv(*_) s b <1> rv2av[t2] lKM/1 c <0> padsv[$ref:FAKE] s d <1> entersub[t3] KS/TARG,1 e <1> leavesub[1 ref] K/REFC,1 main::goto1: f <;> nextstate(main 5 -e:2) v g <0> pushmark sRM h <0> padsv[$ref:FAKE] s i <1> rv2cv[t2] lKRM/33 j <1> refgen sK/1 k <1> goto sKS/1 l <1> leavesub[1 ref] K/REFC,1 main::goto2: m <;> nextstate(main 6 -e:2) v n <0> padsv[$ref:FAKE] s o <1> goto sKS/1 p <1> leavesub[1 ref] K/REFC,1 -e syntax OK
        Miscellaneous points:

        You can see that goto $ref bypasses an unnecessary deref/refgen that goto &$ref does.

        refcall and named have to load @_ onto the stack; the gotos don't.

        refcall and named differ only by one operation: gv vs. padsv. gv and padsv are both trivial ops (except when padsv has flags set that it doesn't here) that just push a value on the stack, so I wouldn't expect any significant difference between them.

        I'm not sure what you're asking, but goto &$ref is no faster than foo(), which is in line with the linked node.

        use strict; use warnings; use Benchmark qw( cmpthese ); sub foo { @_ } my $ref = \&foo; cmpthese(-2, { refcall => sub { @_=(1); $ref->(@_) }, named => sub { @_=(1); foo(@_) }, g_scalar => sub { @_=(1); goto $ref }, g_amp => sub { @_=(1); goto &$ref }, });

        outputs

        Rate named refcall g_amp g_scalar named 811837/s -- -1% -3% -26% refcall 820126/s 1% -- -2% -25% g_amp 834174/s 3% 2% -- -24% g_scalar 1099304/s 35% 34% 32% --