http://www.perlmonks.org?node_id=895401

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

Given:
sub foo { my (undef, $x) = @_; print $_[0], $x; } my $big = 'a' x 99999999; foo($big, 'bar');

Does the list assignment of $_[0] to undef incur any significant runtime cost?

I expect that it doesn't, and would like to know what goes on under the hood.

I can't tell what happens in an aassign:
$ perl -MO=Concise -e '(undef) = $x' 8 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 7 <2> aassign[t1] vKS ->8 - <1> ex-list lK ->5 3 <0> pushmark s ->4 - <1> ex-rv2sv sK/1 ->- 4 <$> gvsv(*x) s ->5 - <1> ex-list lK ->7 5 <0> pushmark s ->6 6 <0> undef sP ->7 -e syntax OK $ perl -MO=Concise -e '($y) = $x' 8 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 7 <2> aassign[t1] vKS ->8 - <1> ex-list lK ->5 3 <0> pushmark s ->4 - <1> ex-rv2sv sK/1 ->- 4 <$> gvsv(*x) s ->5 - <1> ex-list lK ->7 5 <0> pushmark s ->6 - <1> ex-rv2sv sKPRM*/1 ->- 6 <$> gvsv(*y) s ->7 -e syntax OK

Replies are listed 'Best First'.
Re: Is undef list assignment a no-op?
by Anonymous Monk on Mar 25, 2011 at 07:07 UTC
    It doesn't look exactly like a no-op to me, but its close enough :)
    #!/usr/bin/perl -- use strict; use warnings; use Benchmark 'cmpthese'; Main( @ARGV ); exit( 0 ); sub Main { #~ my $big = 'a' x 99999999; # out of memory for me :) my $big = 'a' x 9999999; cmpthese( -3, { 'U' => sub { Fundef($big,'bar'); return; }, 'R' => sub { Ffednu($big,'bar'); return; }, 'V' => sub { Fvaria($big,'bar'); return; }, }); } sub Fundef { my (undef, $x) = @_; my $blah = 0; if($x){ if($_[0]){ $bl +ah=1; } } return; } sub Ffednu { my ( $x ) = $_[1]; my $blah = 0; if($x){ if($_[0]){ $blah +=1; } } return; } sub Fvaria { my ($var, $x ) = $_[1]; my $blah = 0; if($x){ if($_[0]){ +$blah=1; } } return; } __END__
Re: Is undef list assignment a no-op?
by BrowserUk (Patriarch) on Mar 25, 2011 at 11:52 UTC

    I can't help but wonder why you would require the caller to pass a parameter you're never going to use?

    If there is a good reason, (and I cannot think of one), then given the not insignificant difference in performance:

    $a='a'x1e6; $b=12345; cmpthese -1,{ a=>q[ sub{ my( undef, $x ) = @_ }->( $a, $b ) ], b=>q[ sub{ my $x = $_[1] }->( $a, $b ) ], c=>q[ sub{ my( $unused, $x ); }->( $a, $b ) ], };; Rate a b c a 1703708/s -- -19% -20% b 2107186/s 24% -- -1% c 2129207/s 25% 1% --

    I'd be asking myself if I thought that assignment to undef was any clearer than assignment to a variable called $unused?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      If there is a good reason, (and I cannot think of one), then given the not insignificant difference in performance:

      I don't know about good, but I think I've seen this scenario with callbacks, POE/LWP/Tk.... sometimes the first arg is large object, or large string, and you wish to avoid $_[0] or $_[CONSTANT] etc etc

        Good call++


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

      Small oversight - your benchmark with $unused never makes an assignment from @_. When I fixed the typo and reran it, (undef, $x)= @_ was marginally faster than ($unused, $x) = @_ though not by an amount I consider significant (3%). -- Tested on Perl 5.10.0, system Perl for Debian Lenny, 32bit.

      use Benchmark qw(cmpthese); my $a1='a'x1e6; my $b1=12345; cmpthese -1,{ a=>q[ sub{ my( undef, $x) = @_ }->( $a1, $b1 ) ], b=>q[ sub{ my $x = $_[1] }->( $a1, $b1 ) ], c=>q[ sub{ my( $unused, $x ) = @_;}->( $a1, $b1 ) ], }; # Output Rate c a b c 649176/s -- -3% -33% a 670690/s 3% -- -30% b 963764/s 48% 44% --

      Update added Perl version and platform; clarified ranking of results.

        That performance difference seems to be consistent accross all perl versions. I ran the following through 79 perl versions

        use strict; use Benchmark qw(cmpthese); my $a1 = "a" x 1e6; my $b1 = 12345; cmpthese 2000000, { "und" => q[ sub{ my (undef, $x) = @_ }->($a1, $b1) ], "skp" => q[ sub{ my $x = $_[1] }->($a1, $b1) ], "var" => q[ sub{ my ($unused, $x) = @_;}->($a1, $b1) ], };

        I note that 5.13.8 unthreaded is a weird reproducible result:

        Rate var und skp var 1694915/s -- -27% -41% und 2325581/s 37% -- -19% skp 2857143/s 69% 23% --

        Enjoy, Have FUN! H.Merijn

        Acknowledged++ My benchmark was badly borken.

        Unfortunately, so is yours. You need to use our variables for the parameters, otherwise they are not being seen by the compiled code.

        Correct that, and you are in for a big surprise :)


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
      Just as AnonyMonk mentioned. @job, I used the undef assignment as an optimization to avoid an intermediate copy of a large string, but still wanted to assign second arg onwards. foo is a function that wrote the string to a file, and is a code hotspot.

        Yes. I tend to avoid callback interfaces where possible; and pass large parameters, including huge scalars by reference rather than by value.

        But, I realise that we don't always have control over the modules we have to use, or the design choices of their authors.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Is undef list assignment a no-op?
by ikegami (Patriarch) on Mar 25, 2011 at 17:14 UTC
    I've seen the code before. It doesn't create a temp var and do the assignment; it simply skips over it.
Re: Is undef list assignment a no-op?
by anonymized user 468275 (Curate) on Mar 25, 2011 at 11:26 UTC
    Although, you don't actually say what you are trying to do here. For example, if the idea is to remove the first parameter for the purposes of the subroutine, it is more normal just to shift it out and normal ways are more maintainable.

    One world, one people

      It is more normal just to shift it out and normal ways are more maintainable.

      I can respect that it isn't a style you feel comfortable with or are familiar with, but I wouldn't go so far as to call it unusual or abnormal.

      For one, using undef as a placeholder in a list on the left hand of an assignment is a perfectly valid use of Perl syntax. No less than Larry Wall documents its usage in the book Programming Perl (p. 819). Lest one relegate this to some ancient deprecated version of Perl, this usage is also documented even today in undef.

      For another, the use of undef in a list preserves the order of parameters without all the visual noise created by shift statements. One might argue that is is more self-documenting and hence more maintainable.

      Finally, and this is probably most important, shifting parameters out of @_ can make debugging more difficult. If you die and need a stack trace, the shifted out parameters will sometimes display as "undef" being passed to your function call, even though the actual value passed into the function call was absolutely not undef.