tags:

views:

47

answers:

2

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 :

  1. 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 ?
  2. 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

+5  A: 
mobrule
Yay ! Thank you !
OMG_peanuts
+2  A: 

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.

daotoad
You need to change the Mobrule version to use `$hashref->{"MYKEY$_"}` to get an accurate benchmark. Although I expect it won't really change the results.
Ven'Tatsu
Thank you daotoad
OMG_peanuts
Thanks, Ven'Tatsu. Yet another example of how easy it is to try to test one thing, and wind up testing another. I fixed the code and updated the results.
daotoad