ever wondered whether using a series of shift's or a list assignment at the start of a method/function is faster? well, i did. so i benchmarked: 1/3 shift(s); compared to a 1/3 argument assignment to a list; compared to using @_ directly. the function body itself is a trivial integer addition, so as not to detract from the point of the case study.

so, here are the results (re-formatted slightly for readability):

Benchmark: timing 2000000 iterations of argumentative_1arg, argumentat +ive_3args, direct_1arg, direct_3args, shifty_1arg, shifty_3args... argumentative_1arg: 16 wallclock secs (15.85 usr + 0.03 sys = 15.88 CPU) @ 125944.58/s argumentative_3args: 21 wallclock secs (19.45 usr + 0.01 sys = 19.46 CPU) @ 102774.92/s direct_1arg: 11 wallclock secs ( 9.75 usr + 0.01 sys = 9.76 CPU) @ 204918.03/s direct_3args: 12 wallclock secs (11.15 usr + 0.03 sys = 11.18 CPU) @ 178890.88/s shifty_1arg: 16 wallclock secs (16.44 usr + -0.01 sys = 16.43 CPU) @ 121728.55/s shifty_3args: 23 wallclock secs (21.91 usr + 0.02 sys = 21.93 CPU) @ 91199.27/s

in summary - there's bugger-all difference between using shifts and assigning to a list. using the contents of @_ directly is, naturally, faster, but not overwhelmingly so - certainly not enough to justify the potential obfuscation (and danger!) factor of using @_ directly.

here's the code that was used:

#!/usr/bin/perl -w use Benchmark; use strict; package ArgTest; sub shifty_1arg { my $this = shift; my $first_arg = shift; $$this += $first_arg; } sub shifty_3args { my $this = shift; my $first_arg = shift; my $second_arg = shift; my $third_arg = shift; $$this += $first_arg + $second_arg + $third_arg; } sub argumentative_1arg { my( $this, $first_arg ) = @_; $$this += $first_arg; } sub argumentative_3args { my( $this, $first_arg, $second_arg, $third_arg ) = @_; $$this += $first_arg + $second_arg + $third_arg; } sub direct_1arg { ${$_[0]} += $_[1]; } sub direct_3args { ${$_[0]} += $_[1] + $_[2] + $_[3]; } # main /------------------------------------------------ package main; my $total; bless( my $object = \$total, 'ArgTest' ); my @args = ( 1 .. 3 ); timethese( 100_000, { shifty_1arg => sub { $object->shifty_1arg( @args ) }, argumentative_1arg => sub { $object->argumentative_1arg( @ar +gs ) }, direct_1arg => sub { $object->direct_1arg( @args ) }, shifty_3args => sub { $object->shifty_3args( @args ) } +, argumentative_3args => sub { $object->argumentative_3args( @a +rgs ) }, direct_3args => sub { $object->direct_3args( @args ) } +, } ); print "total: $total\n"; exit;

Replies are listed 'Best First'.
Re: benchmark for shift vs list assignment for object methods
by PodMaster (Abbot) on Jul 28, 2002 at 10:29 UTC
    Your benchmark is interesting, however, sadly, completely useless.

    perldoc -f shift:

    shift   Shifts the first value of the array off and returns it
            shortening the array by 1 and moving everything down.
    
    You see that, shift modifies the array (as i'm sure you already know). Thus, the benchmark is void.

    On another note, the danger of using @_ directly is the same as that of doing practically anything in perl (there's plenty of rope to hang yourself).

    I ++ you symbolically for experimenting, and satisfying your curiosity (with code ;).

    update: an interesting read, related to the subject of benchmarking and "efficiency", is Premature optimization by Curtis POE

    update: Also, I like cmpthese so much more for benchmarks, in my version of Benchmark.pm, it's exported by default ;) (see below for code)

    update: Another monk raised the question of why I think this benchmark is, well, pointelss. Is think it's like comparing apples and oranges. Since oranges do more work, and do different things than apples, why would you compare them?

    #!/usr/bin/perl -w use Benchmark qw( cmpthese ); # for sake of others ;) use strict; package ArgTest; sub shifty_1arg { my $this = shift; my $first_arg = shift; $$this += $first_arg; } sub shifty_3args { my $this = shift; my $first_arg = shift; my $second_arg = shift; my $third_arg = shift; $$this += $first_arg + $second_arg + $third_arg; } sub argumentative_1arg { my( $this, $first_arg ) = @_; $$this += $first_arg; } sub argumentative_3args { my( $this, $first_arg, $second_arg, $third_arg ) = @_; $$this += $first_arg + $second_arg + $third_arg; } sub direct_1arg { ${$_[0]} += $_[1]; } sub direct_3args { ${$_[0]} += $_[1] + $_[2] + $_[3]; } # main /------------------------------------------------ package main; my $total; bless( my $object = \$total, 'ArgTest' ); my @args = ( 1 .. 3 ); cmpthese( 1_000_000, { shifty_1arg => sub { $object->shifty_1arg( @args ) }, argumentative_1arg => sub { $object->argumentative_1arg( @ar +gs ) }, direct_1arg => sub { $object->direct_1arg( @args ) }, shifty_3args => sub { $object->shifty_3args( @args ) } +, argumentative_3args => sub { $object->argumentative_3args( @a +rgs ) }, direct_3args => sub { $object->direct_3args( @args ) } +, } ); print "total: $total\n"; __END__ Benchmark: timing 1000000 iterations of argumentative_1arg, argumentat +ive_3args, direct_1arg, direct_3args, shifty_1arg, shifty_3args... argumentative_1arg: 1 wallclock secs ( 1.36 usr + 0.00 sys = 1.36 C +PU) @ 735835.17/s (n=1000000) argumentative_3args: 3 wallclock secs ( 1.80 usr + 0.00 sys = 1.80 +CPU) @ 556173.53/s (n=1000000) direct_1arg: 0 wallclock secs ( 1.00 usr + 0.00 sys = 1.00 CPU) @ 1 +000000.00/s (n=1000000) direct_3args: 1 wallclock secs ( 1.19 usr + 0.00 sys = 1.19 CPU) @ +841750.84/s (n=1000000) shifty_1arg: 3 wallclock secs ( 1.59 usr + 0.00 sys = 1.59 CPU) @ 6 +27746.39/s (n=1000000) shifty_3args: 3 wallclock secs ( 2.34 usr + 0.00 sys = 2.34 CPU) @ +426621.16/s (n=1000000) Rate shifty_3args
    argumentative_3args shifty_1arg argumentative_1arg direct_3args direct_1arg
    shifty_3args         426621/s           --                -23%        -32%               -42%         -49%        -57%
    argumentative_3args  556174/s          30%                  --        -11%               -24%         -34%        -44%
    shifty_1arg          627746/s          47%                 13%          --               -15%         -25%        -37%
    argumentative_1arg   735835/s          72%                 32%         17%                 --         -13%        -26%
    direct_3args         841751/s          97%                 51%         34%                14%           --        -16%
    direct_1arg         1000000/s         134%                 80%         59%                36%          19%          --
    total: 21000000

    ____________________________________________________
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      I fail to see why this bit of exploration deserves to be described as useless, pointless or void. it takes a few idioms often used for the same purpose, each with surprisingly determined adherents, and tries to find out which is quickest. The results seem valid and well presented, if not very startling. What's the problem?

      If your point is that these approaches have very different side effects and that people in the habit of using (...) = @_ and &func are going to need to know that, then I quite agree, but it doesn't invalidate the benchmark.

      If the problem is in the methodology then perhaps you could be a little more specific, for the benefit of people like me who cannot see?

      shift is indeed modifying the array, but keep in mind this is the @_ array, not a globally used one. It does make a difference if you later in your subroutine would use @_, but that doesn't happen that often.

      List assignment and shifting off from @_ are two very commonly used techniques to put the subroutine arguments into variables. And since @_ is not used further in the subroutine, the fact that @_ is or isn't modified is irrelevant.

      Abigail

      local @_ =  @_; #UPDATE added assignment

      --
      perl -pew "s/\b;([mnst])/'$1/g"

      >You see that, shift modifies the array (as i'm sure you already know). Thus, the benchmark is void.

      Huh? Where is the problem? You can modify test internal data as much as you want, as long as it is initialized the same way for each test. It is no problem to modify @_ here. There would be a problem if he would directly modify the global @args. Which he doesn't do. Or are you trying to indicate that

      my @arr = ('a', 'b', 'c'); sub dosth { my $first = shift; } dosth(@arr); print "@arr";

      results in "a b"? Of course not, right?

Re: benchmark for shift vs list assignment for object methods
by sauoq (Abbot) on Jul 28, 2002 at 19:01 UTC

    Your results are hardly surprising but I disagree with podmaster that the benchmark is "useless." The whole point of benchmarking is to compare different ways of doing things. Sometimes the tasks which have more steps and seem like more work are handled more efficiently by the underlying architecture.

    This just isn't one of those cases.

    I don't think that the results here are at all sufficient to recommend using list assignment over shift. That's still a sylistic concern. Your functions will almost certainly be complex enough to make the task of retrieving your arguments relatively insignificant. It's likely that if a few microseconds really matter you should either inline that code rather than use a sub or, better yet, just start rewriting your script in assembly.

    -sauoq
    "My two cents aren't worth a dime.";