Did you know, if you pass an argument to a Perl subroutine, the value in @_ is an alias for the value you passed in? I didn't! This is very useful, because actual this is a "call by reference" and it should be fast, because there is no copying in the subroutine and no setting the value from the subroutine's return.
Example for a call by reference:
sub increment { $_[0]++; } my $foo = 1; increment($foo); # $foo is now 2
Example for a call by value:
sub increment { return($_[0]+1); } my $foo = 1; $foo = increment($foo); # $foo is now 2
So I benchmarked the following code:
#!/usr/bin/perl use 5.10.0; use Benchmark; # call by reference sub call_by_reference { $_[0]+=1; } # call by value sub call_by_value { return($_[0]+1); } timethese(10000000, { 'call_by_ref' => sub { my $foo = 1; call_by_reference($foo); }, 'call_by_val' => sub { my $foo = 1; $foo = call_by_value($foo); }, });
And YES call by reference is faster, nearly 25% faster.
Results:
Benchmark: timing 10000000 iterations of call_by_ref, call_by_val... call_by_ref: 7 wallclock secs ( 7.95 usr + 0.00 sys = 7.95 CPU) @ 1257861.64/s (n=10000000) call_by_val: 10 wallclock secs (10.50 usr + 0.00 sys = 10.50 CPU) @ 952380.95/s (n=10000000)
So, working with references is fast and cheap, but that's nothing new. What I like to know, is there a real difference working directly on @_. I wrote this little test, to prove it.
#!/usr/bin/perl use 5.10.0; use Benchmark; # call by reference on @_ sub call_by_reference_array { $_[0]++; } # call by reference with ref copy to scalar sub call_by_reference_ref { my($ref) = @_; $$ref++; } timethese(10000000, { 'call_by_ref_array' => sub { my $foo = 1; call_by_reference_array($foo); }, 'call_by_ref_ref' => sub { my $foo = 1; call_by_reference_ref(\$foo); }, });
Results:
Benchmark: timing 10000000 iterations of call_by_ref_array, call_by_ref_ref... call_by_ref_array: 3 wallclock secs ( 3.10 usr + 0.00 sys = 3.10 CPU) @ 3225806.45/s (n=10000000) call_by_ref_ref: 3 wallclock secs ( 4.61 usr + 0.00 sys = 4.61 CPU) @ 2169197.40/s (n=10000000)
As you can see, only by copying the passed reference to a scalar in the subroutine, you lose performance. And not only a few operations per second, you lose 1056609.05/s.



