Seitenanfang

Passing arguments

Many functions, methods or subs (no matter how you call them) need some arguments. In Perl TIMTOWTDI, but some are faster than others. I'll compare eight ways to get arguments passed to a sub.

race-92193_1280.jpgThe first doesn't actually copy the args, but only saves the reference to the argument array in a variable:

sub array_ref {
my $args = \@_;
return $args->[0];
}

The second assigns the values of the argument array to individual variables:

sub array_copy {
my ($first, $second, $third) = @_;
return $first;
}

Shift off the values into individual variables one by one and remove them from the argument array:

sub shift_off {
my $first = shift;
my $second = shift;
my $third = shift;
return $first;
}

Copy the whole argument array to a self-defined variable:

sub copy_once {
my @args = @_;
return $args[0];
}

Copy the whole argument array and assign each value to a named variable:

sub copy_twice {
my @args = @_;
my $first = $args[0];
my $second = $args[1];
my $third = $args[2];
return $args[0];
}

Pass the values using named arguments and copy all of them into one arguments hash:

sub named {
my %args = @_;
return $args{first};
}

Pass the values using named arguments and copy them to named variables:

sub named_copy {
my %args = @_;
my $first = $args{first};
my $second = $args{second};
my $third = $args{third};
return $first;
}

Let Moose and its helper modules assign the values into named variables:

class Bench::Moose {
method moose(Int $first, Str $second, Any $third) {
return $first;
}
}

I expect the first way, array_ref to be the quickest and Moose to be the slowest. Moose also enforces some data types for the values which slows down things even more. I included it just to see the results.

Rate
moose 111.310/s
named_copy 783.942/s
named 1.089.026/s
copy_twice 1.208.956/s
copy_once 1.767.704/s
array_ref 2.214.928/s
shift_off 2.367.481/s
array_copy 2.432.775/s

I didn't expect named arguments to be that slow. The additional copy to named variables drop them to less than one third of the fastest way. Moose is even slower than I expected, but isn't comparable to the others, because so much additional stuff is being done (type checks, $self init, some method wrappers, etc.).

Here is sub_arguments_benchmark.zip.

 

14 Kommentare. Schreib was dazu

  1. joris

    Something's wrong with the named method.
    The 1st arg will be a key, and the 2nd arg will be a value.

    • Sebastian

      Sure, they are. I didn't show the calls, but the named methods need to be called first => 1, second => 2, etc.

  2. How about including passing in a hashref?

    • Sebastian

      Good point. A simple $hashref = shift or ($hashref) = @_ might be the fastest.

  3. Brad Gilbert

    Actually if you used 5.18 or newer version of Perl; the array_copy version should be quite a bit faster as it compiles down to much fewer ops. I believe it is only one or two ops for any number of variables.

  4. Hi!

    It's a nice blog entry which I found on Perl Weekly. I'd like to report a typo though: "Let Moose and it's helper modules assign the values into named variables:" ==> "it's" should be "its". I wanted to report it by E-mail, but couldn't find an E-mail anywhere on this page, and a lot of the user interface still appears to be in German (even though my browser is configured with Accept-Language only in English and Hebrew) .

    Regards,

    -- Shlomi Fish

    • Sebastian

      Thank you, I'll fix the typo.
      Apologies for the mostly-German texts, I'll try to fix that too.

  5. Alan Rocker

    Interestingly counter-intuitive. What's array_ref up to, that slows it relative to array_copy? Wouldn't there be an effect depending on the size of the array?

    • Sebastian

      Adding another 42 elements (even if 42 positional arguments would be hell for the developers):
      array_ref 1125443/s
      array_copy 2146566/s

      Todays results with the original values:
      array_ref 1955259/s
      array_copy 2556077/s

      Results with 11k of stuff for the second arg instead of 11 bytes:
      moose 96517/s
      named_copy 452184/s
      copy_twice 605390/s
      named 728739/s
      copy_once 1018043/s
      shift_off 1124159/s
      array_copy 1173526/s
      array_ref 2233948/s

  6. Thanks, Sebastian.

  7. Lars

    Nice blog. Thanks!

    I'd rather compare Method::Signatures / func than Moose methods.

    So I added:

    func m_func($first, $second, $third)
    func m_func_tc(Int $first, Str $second, Any $third)

    and got these results:

    m_func_tc 246556/s
    named_copy 551437/s
    named 681506/s
    copy_twice 927486/s
    m_func 1274059/s
    copy_once 1315157/s
    array_ref 1557008/s
    shift_off 1675464/s
    array_copy 1860920/s

  8. WK

    Hi!

    There is also possible to use `@_` directly and my benchmark with your code show it is twice as fast than `array_copy`-version.

    Thank you for your post,

    Gunnar

    • Sebastian

      I didn't include plain @_ because it isn't guranteed to keep it's value if the sub calls other subs. Hunting down bugs based on hidden unexpected @_ manipulation could be very time consuming and I don't use @_ directly for subs longer than two or three lines.

      • WK

        Exactly! It is for very short code. I think, that different ways are for different situations and it is good to know how passing arguments inflicts speed. For example, today I wrote simple function to format number like this:

        sub nm {
        sprintf( '%.*f', 2, $_[0] );
        }

        Here is no need to assign argument to var, but as very frequently used function it may have pretty good win. But having more complicated function you can't rely on `@_` and must choose some other way you described above. And even one may be much slower than other, it has it's own right place to use. So is with `@_` too.

Schreib was dazu

Die folgenden HTML-Tags sind erlaubt:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>