Reputation: 15967
Parameters to perl subs are passed in @_. To make my programs easier to read, I've always used this pattern to get named parameters:
sub foo1 {
my ($bar, $baz) = @_;
do_something($bar,$baz);
}
but it causes $_[0]
and $_[1]
to be copied. If I were to access $_[0]
directly instead of accessing $bar
in the above pattern, I have call-by-value/alias access to the callers parameters with the usual caveats of call-by-reference, but it is much faster (see demo below).
I'm left with this suspicion, that the my ($param1, $param2 ...) = @_;
pattern is bad for performance reasons. So I find I have to choose between fast and readable programs which is, well, an impossible choice.
I end up writing subs where performance is the focus with $_[<n>]
and everything else with the pattern above. Trouble is, often I don't know beforehand where the bottlenecks are ;-)
Is there a way to get named parameters that are also fast? Or what seems to be canon on the matter? $_[0]
or $bar
?
use Time::HiRes qw(time);
# Lets just do *something* with the parameters - here we just add up all
# their lengths
my $totalLength = 0;
sub foo1 {
# Access $_[0] directly - effectively call-by-reference
$totalLength += length($_[0]);
}
sub foo2 {
# Access a copy of $_[0] - effectively call-by-value - involves
# copying
my ($bar) = @_;
$totalLength += length($bar);
}
my $a = 'b' x 10_000;
my $t0 = time;
foreach (0..1_000_000) {
foo1($a);
}
printf "foo1 %2.6f\n", time - $t0;
$t0 = time;
foreach (0..1_000_000) {
foo2($a);
}
printf "foo2 %2.6f\n", time - $t0;
Prints out
foo1 0.329470
foo2 1.280364
foo1 is almost 4 times faster than foo2 because it avoids copying $_[0]
.
Upvotes: 1
Views: 128
Reputation: 129491
Pass them via a hashref:
my %args = (bar=>1, baz=>2);
mySub(\%args);
sub mySub {
my $args = shift;
doSomething($args); # pass entire parameter list for cheap!
doSomething2($args{bar}); # Use specific parameter
}
Frankly, I have my slight doubts about the performance benefits (hash access isn't free) but you can benchmark it if you really need to. But this isn't the best performance option (see below) and may not even be needed (see last part), and this I don't see much need to try.
Another option (which kinda sucks but is better for performance) is to use $_[1]
etc... but combat non-readability via extensive comments.
# pass `baz`
doSomething($_[1]);
Another even-higher-performance-but-bad-design option is to bypass the parameter passing alltogether, and use global variables to pass parameters.
our $bar = 1;
mySub();
sub mySub {
#do something with $bar. Pray some other code didn't clobber it
}
Last consideration:
If your code is so well-tuned AND so performance-sensitive that copying a couple of scalars makes a significant difference, you MAY want to drop out of Perl into pure C for those functions.
But, as Knuth said, please don't optimize prematurely.
First, profile your entire app and make sure that the scalar parameter copying is indeed where your biggest bottlenecks are. I don't dispute that this is plausible, but typically, bottlenecks are elsewhere (IO, DB, slow data structures, etc...).
In other words, the fact that $operation_X
can be implemented 4 times faster, means nothing if $operation_X
takes up a total of 0.01% of your runtime. Speeding it up by a factor of 4 is simply not worth the trouble given decreased readability.
Upvotes: 6
Reputation: 378
Well, if you're passing $bar and $baz, in that order, to sub do_something(), another bad option is to use the following scary -- but documented -- syntax:
sub foo1 { goto &do_something}
...which would pass the context on to do_something() immediately. No help with documenting the parameters, but it's possibly the fastest pass-on-to-another-routine mechanism of the bunch. :-)
Heck, I'd downvote this answer myself....
Upvotes: 1