I would like to know theoretical background of hash values performing better than keys, and whether perldoc covers this topic.
use strict;
use warnings;
use Benchmark qw(:all);
my %hash = 1 .. 30_000;
cmpthese(-3, {
values => sub {
for my $v (values %hash) { }
},
keys => sub {
for my $v (keys %hash) { }
},
avalues => sub {
for my $v (@{[ values %hash ]}) { }
},
akeys => sub {
for my $v (@{[ keys %hash ]}) { }
},
});
__DATA__
Rate akeys keys avalues values
akeys 196/s -- -33% -56% -71%
keys 294/s 50% -- -35% -57%
avalues 451/s 130% 54% -- -33%
values 677/s 245% 131% 50% --
perl is 5.20.0
The keys aren't stored in the hash as scalar. A string scalar must therefore be created for each key returned by keys
.
On the other hand, the values are stored as scalars in the hash, and those very scalars are returned by values
. Nothing copied but a pointer.
The above can be demonstrated using using the following benchmark:
use strict;
use warnings;
use Benchmark qw( cmpthese );
my %sm_hash = map { sprintf('%04d', $_) } 1 .. 1000*2;
my %lg_hash = map { ( "x" x 10_000 ) . sprintf('%04d', $_) } 1 .. 1000*2;
cmpthese(-3, {
sm_values => sub { 1 for values %sm_hash },
lg_values => sub { 1 for values %lg_hash },
sm_keys => sub { 1 for keys %sm_hash },
lg_keys => sub { 1 for keys %lg_hash },
});
Output:
Rate lg_keys sm_keys lg_values sm_values
lg_keys 5286/s -- -19% -57% -58%
sm_keys 6532/s ==> 24% -- -47% -48%
lg_values 12247/s 132% 87% -- -2%
sm_values 12517/s 137% 92% ==> 2% --
As you can see, then length of the keys has an effect on how long it takes for keys
to be evaluated, but the length of the values has no effect on how long it takes for values
to be evaluated.
A simpler but less definitive demonstration comes in the form of an attempt to modify the values returned by keys
and values
.
for (keys(%h)) {
$_ = uc($_); # Has no effect on the hash.
}
for (values(%h)) {
$_ = uc($_); # Effects the hash.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With