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.
The 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-
joris
18.04.2014 19:12
Antworten
-
Sebastian
20.04.2014 10:21
Antworten
-
Ron Savage
19.04.2014 0:52
Antworten
-
Sebastian
20.04.2014 10:19
Antworten
-
Brad Gilbert
20.04.2014 0:31
Antworten
-
Shlomi Fish
21.04.2014 16:30
Antworten
-
Sebastian
22.04.2014 6:30
Antworten
-
Alan Rocker
21.04.2014 20:11
Antworten
-
Sebastian
22.04.2014 6:47
Antworten
-
Shlomi Fish
22.04.2014 8:58
Antworten
-
Lars
24.04.2014 8:02
Antworten
-
WK
30.04.2014 9:44
Antworten
-
Sebastian
30.04.2014 12:11
Antworten
-
WK
30.04.2014 12:41
Antworten
Something's wrong with the named method.
The 1st arg will be a key, and the 2nd arg will be a value.
Sure, they are. I didn't show the calls, but the named methods need to be called first => 1, second => 2, etc.
How about including passing in a hashref?
Good point. A simple $hashref = shift or ($hashref) = @_ might be the fastest.
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.
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
Thank you, I'll fix the typo.
Apologies for the mostly-German texts, I'll try to fix that too.
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?
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
Thanks, Sebastian.
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
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
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.
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.