UPDATE: Please don't trust the benchmark, turns out, that when operating on the same hash they are sabotaging each other*
> Is (keys %hash)[$idx++] optimized to be performant, or will it create a long list of keys to be thrown away?
it's way faster than each !?!
UPDATE Sorry I misread the results, it's late. :)
The approach with (keys %hash)[$idx++] is far slower.
use 5.12.0;
use strict;
use warnings;
use Data::Dump qw/pp dd/;
use Benchmark qw/cmpthese/;
# --------- fetch all defined generators
my %generators;
for my $generator ( grep {/^gen_/} keys %Generators::) {
my $g_generator = $Generators::{$generator};
$generators{$generator} = $g_generator;
}
#warn pp \%generators;
# --------- init test data
my %hash;
my @entries = 0..1e3;
@hash{@entries} = reverse @entries;
my %copy = %hash;
# --------- run tests
while( my ($gen,$glob) = each %generators ) {
say "\n*** Testing $gen";
Tests::all(
"$gen",
$glob->(\%hash),
\%copy
);
}
Test::More::done_testing();
# --------- run benchmarks
# --- expand test data
@entries = 0..1e6;
@hash{@entries} = reverse @entries;
# --- init iterators
my %iterators;
while ( my ($gen,$glob) = each %generators ) {
(my $name = $gen) =~ s/^gen_//;
$iterators{$name} = $glob->(\%hash);
}
#warn pp \%iterators;
# --- benchmark
cmpthese(-1, \%iterators );
package Generators;
sub gen_copy_keys {
my ($h_hash) = @_;
my $idx = 0;
my @keys = keys %$h_hash;
return sub {
# --- reset?
if ($idx >= @keys) {
$idx = 0;
return;
}
# --- return key, value
my $key = $keys[$idx++];
return $key, $h_hash->{$key};
}
}
sub gen_copy_hash_and_each {
my ($h_hash) = @_;
my %copy = %$h_hash;
return sub {
# --- return key, value
return each %copy
}
}
sub gen_each {
my ($h_hash) = @_;
return sub {
# --- return key, value
return each %$h_hash;
}
}
sub gen_idx_keys {
my ($h_hash) = @_;
my $idx = 0;
return sub {
if ($idx >= keys %$h_hash) {
$idx = 0;
return;
}
# --- return key, value
my $key = (keys %$h_hash)[$idx++];
return $key, $h_hash->{$key};
}
}
package Tests;
use Test::More;
sub all {
my ( $name, $c_iter, $h_expected ) = @_;
# --- gathers all entries
my %got = ();
while ( my ($k,$v) = &$c_iter ) {
$got{$k} = $v;
}
is_deeply(\%got, $h_expected, "'$name' iterates over all entries")
+;
# --- cycles to restart
%got = ();
while ( my ($k,$v) = &$c_iter ) {
$got{$k} = $v;
}
is_deeply(\%got, $h_expected, "'$name' cycles and iterates again")
+;
}
Compilation started at Fri Jun 19 02:17:48
C:/Perl_524/bin\perl.exe d:/exp/hash_iter.pl
*** Testing gen_idx_keys
ok 1 - 'gen_idx_keys' iterates over all entries
ok 2 - 'gen_idx_keys' cycles and iterates again
*** Testing gen_copy_hash_and_each
ok 3 - 'gen_copy_hash_and_each' iterates over all entries
ok 4 - 'gen_copy_hash_and_each' cycles and iterates again
*** Testing gen_copy_keys
ok 5 - 'gen_copy_keys' iterates over all entries
ok 6 - 'gen_copy_keys' cycles and iterates again
*** Testing gen_each
ok 7 - 'gen_each' iterates over all entries
ok 8 - 'gen_each' cycles and iterates again
1..8
Rate idx_keys copy_keys each copy_has
+h_and_each
idx_keys 3.05/s -- -100% -100%
+ -100%
copy_keys 2361328/s 77510489% -- -6%
+ -23%
each 2522717/s 82808095% 7% --
+ -17%
copy_hash_and_each 3047966/s 100049391% 29% 21%
+ --
Compilation finished at Fri Jun 19 02:18:05
| [reply] [d/l] [select] |