Variable instanciation VS double hash reference access
Initially, I was looking for a fast way to access a hash ref element (with a default value if no value is available).
So I tried the following :
use strict;
use warnings;
use DateTime;
my $hashref = { };
for (0..249) {
my $lIdx = $_ * 2;
$hashref->{"MYKEY$lIdx"} = "MYVAL$lIdx";
}
sub WithVariable{
my $result = $hashref->{"MYKEY$_[0]"};
return defined $result ? $result : "NONE";
}
sub WithoutVariable{
return defined $hashref->{"MYKEY$_[0]"} ? $hashref->{"MYKEY$_[0]"} : "NONE";
}
$|++;
my @preciousvalues1 = ();
my @preciousvalues2 = ();
my $dt = DateTime->now;
for (1..25000) { for (0..498) { push @preciousvalues1, WithVariable($_) } }
my $lag开发者_如何学运维 = DateTime->now - $dt;
print "With a variable: ", $lag->seconds, "\n";
$dt = DateTime->now;
for (1..25000) { for (0..498) { push @preciousvalues2, WithoutVariable($_) } }
$lag = DateTime->now - $dt;
print "Without a variable: ", $lag->seconds, "\n";
print "Done\n";
But the results seem to be quite random, and perl seems to do a lots of stuff after printing the "Done", and takes forever to exit.
My questions are :
- What would be a neat implementation of a function allowing to access values stored in a hash with a default value if no value is found ?
- What the hell is perl doing after it printed "Done" ? Garbage collection ?
Perl version : This is perl, v5.10.1 built for MSWin32-x86-multi-thread
- Since version 5.10 you can use the
//
(defined-or) operator to test for defined-ness and assign a default value in one step.
$result = $hashref->{$key} // "NONE"
sets $result
to $hashref->{$key}
if that value is defined, and "NONE"
otherwise.
2. More or less. If it bothers you, slap a
use POSIX; POSIX::_exit(0);
at the end of your script. This will end your script immediately, bypassing Perl's normal shutdown routine (and any code you have put into END { }
blocks). It's probably not a big deal to use it on this script, but it would normally not be a best practice to use _exit
.
As I already said in a comment, use Benchmark to do these sorts of comparisons. Benchmark handles all the fiddly details of timing the tests and printing a report.
Another thing worth mentioning is that your test is not testing what you think it is. Your tests push values onto @preciousvalues1
and @preciousvalues2
. Each test iteration adds approximately 500 results to one of the arrays. Since you don't clear the arrays between iterations, you wind up with two arrays each with around 12.5 million entries. I guess that this is driving you to swap, and leading you to slow, semi-random execution times. It is also the cause of the delay on exiting your program. You've allocated a massive amount of RAM to those structures. Perl wants to tear them down properly, and ensure that any destructors or END blocks are triggered.
Here's a cleaned up version of your test that focuses on hash access. I've added mobrule's solution for comparison.
use strict;
use warnings;
use Benchmark qw(cmpthese);
my $hashref = { };
for (0..249) {
my $lIdx = $_ * 2;
$hashref->{"MYKEY$lIdx"} = "MYVAL$lIdx";
}
sub WithVariable{
my $result = $hashref->{"MYKEY$_[0]"};
return defined $result ? $result : "NONE";
}
sub WithoutVariable{
return defined $hashref->{"MYKEY$_[0]"} ? $hashref->{"MYKEY$_[0]"} : "NONE";
}
cmpthese( 25000, {
"With Var" => sub {
my @vals;
push @vals, WithVariable($_) for 0..498;
},
"Without Var" => sub {
my @vals;
push @vals, WithoutVariable($_) for 0..498;
},
"Mobrule" => sub {
my @vals;
push @vals, $hashref->{"MYKEY$_"} // 'NONE' for 0..498;
}
} );
With the following results on my system:
Rate With Var Without Var Mobrule
With Var 1382/s -- -0% -49%
Without Var 1389/s 1% -- -49%
Mobrule 2700/s 95% 94% --
It's pretty clear that mobrule's solution is faster, and that there is only a tiny difference between the other two implementations. The big difference is that mobrule's solution does not do a function call that the other two implementations use.
精彩评论