http://qs321.pair.com?node_id=213125

I often use very deeply nested structures in my code. This can make for ugly code. $self->{thiskey}{theykey}{etc}="etc..."
I've been going back and cleaning up some older code by taking a ref to the HoHoHoHo..s and it got me to thinking about hashes in general( That and reading perlguts).

I always assumed that when you said $hash{'constant_key'}->{key2}, Perl would optimize this on compile and not keep fetching $hash{'constant_key'} after it first encountered it.

I thought wrong.
It would appear that using a reference to the desired "Hash in a Hash" has a fairly large performance implication. This very well may be a "Duh!" for others but for me this was very eye opening. Using a reference to a HoH speeds things up on average about 15%! A more practical test (HoH) bears this out. Not a bad speedup. On HoHoH it averages about 35% faster. The deeper the reference, the bigger the payoff.

I don't do a lot of benchmarking so if someone sees a flaw in my method, I'd appreciate feedback.

Lesson: So not only does using refs to nested data structures make your code easier to read, it makes it a good bit faster.
#!/usr/bin/perl use strict; use warnings; use Benchmark qw(cmpthese timethese); my $char = 'a'; my %hash = map {$char++.$_ => $_} (1..50); my %HoH = map {$char++.$_ => {%hash} } (1..20); my %HoHoH = map {$char++.$_ => {%HoH} } (1..50); my ($ref,$ref1,$ref2,$ref3,$ref4); $ref1->{key1} = {%hash}; $ref2->{key1}{key2} = {%hash}; $ref3->{key1}{key2}{key3} = {%hash}; $ref4->{key1}{key2}{key3}{key4} = {%hash}; my ($shortref,$shortref1,$shortref2,$shortref3,$shortref4) = ( \%hash, $ref1->{key1}, $ref2->{key1}{key2}, $ref3->{key1}{key2}{key3}, $ref4->{key1}{key2}{key3}{key4}, ); my $iterations = 10000; my @benchmarks = ( {count=> $iterations, hash=>sub { foreach my $k ( keys %ha +sh ) { $hash{$k}++ } }, ref=>sub { foreach my $k ( keys %{$shortref} ) { $shortre +f->{$k}++ } } }, {count=> $iterations, ref2hash1=>sub { foreach my $k ( key +s %{$ref1->{key1}}) { $ref1->{key1}{$k}++ }}, shortref1=>sub { foreach my $k ( keys %{$shortref1}) { $s +hortref1->{$k}++ }} }, {count=> $iterations, ref2hash2=>sub { foreach my $k ( key +s %{$ref2->{key1}{key2}}) { $ref2->{key1}{key2}{$k}++ }}, shortref2=>sub { foreach my $k ( keys %{$shortref2}) { $s +hortref2->{$k}++ }} }, {count=> $iterations, ref2hash3=>sub { foreach my $k ( key +s %{$ref3->{key1}{key2}{key3}}) { $ref3->{key1}{key2}{key3}{$k}++ }}, shortref3=>sub { foreach my $k ( keys %{$shortref3}) { $s +hortref3->{$k}++ }} }, {count=> $iterations, ref2hash4=>sub { foreach my $k ( key +s %{$ref4->{key1}{key2}{key3}{key4}}) { $ref4->{key1}{key2}{key3}{key +4}{$k}++ }}, shortref4=>sub { foreach my $k ( keys %{$shortref4}) { $s +hortref4->{$k}++ }} }, { count=>2000, HoH =>sub { foreach my $k (keys %HoH) { foreach (keys %{ $HoH{$k} } ){ $HoH{$k}->{$_}++; } } }, refHoH =>sub { foreach my $k (keys %HoH) { my $hashref = $HoH{$k}; foreach (keys %{ $hashref } ){ $hashref->{$_}++; } } }, }, { count=>100, HoHoH =>sub { # Traverse foreach my $k (keys %HoHoH) { foreach my $k2 (keys %{ $HoHoH{$k} } +){ foreach (keys %{ $HoHoH{$k}->{$k2} + } ){ $HoHoH{$k}{$k2}->{$_}++; } } } }, refHoHoH =>sub { # Traverse foreach my $k (keys %HoHoH) { my $hashref = $HoHoH{$k}; foreach (keys %{ $hashref } ){ my $hashref2 = $hashref->{$_}; foreach (keys %{$hashref2} ){ $hashref2->{$_}++; } } } }, }, ); foreach my $bench (@benchmarks){ my $results = timethese(delete $bench->{count} ,$bench); print "-" x 20,"\n"; cmpthese($results); print "-" x 40,"\n\n"; } __END__ Benchmark: timing 10000 iterations of hash, ref... hash: 2 wallclock secs ( 2.06 usr + 0.00 sys = 2.06 CPU) @ 48 +54.37/s (n=10000) ref: 2 wallclock secs ( 2.18 usr + 0.00 sys = 2.18 CPU) @ 45 +87.16/s (n=10000) -------------------- Rate ref hash ref 4587/s -- -6% hash 4854/s 6% -- ---------------------------------------- Benchmark: timing 10000 iterations of ref2hash1, shortref1... ref2hash1: 3 wallclock secs ( 2.47 usr + 0.00 sys = 2.47 CPU) @ 40 +48.58/s (n=10000) shortref1: 3 wallclock secs ( 2.20 usr + 0.00 sys = 2.20 CPU) @ 45 +45.45/s (n=10000) -------------------- Rate ref2hash1 shortref1 ref2hash1 4049/s -- -11% shortref1 4545/s 12% -- ---------------------------------------- Benchmark: timing 10000 iterations of ref2hash2, shortref2... ref2hash2: 3 wallclock secs ( 2.90 usr + 0.00 sys = 2.90 CPU) @ 34 +48.28/s (n=10000) shortref2: 2 wallclock secs ( 2.14 usr + 0.00 sys = 2.14 CPU) @ 46 +72.90/s (n=10000) -------------------- Rate ref2hash2 shortref2 ref2hash2 3448/s -- -26% shortref2 4673/s 36% -- ---------------------------------------- Benchmark: timing 10000 iterations of ref2hash3, shortref3... ref2hash3: 3 wallclock secs ( 3.35 usr + 0.00 sys = 3.35 CPU) @ 29 +85.07/s (n=10000) shortref3: 3 wallclock secs ( 2.23 usr + 0.00 sys = 2.23 CPU) @ 44 +84.30/s (n=10000) -------------------- Rate ref2hash3 shortref3 ref2hash3 2985/s -- -33% shortref3 4484/s 50% -- ---------------------------------------- Benchmark: timing 10000 iterations of ref2hash4, shortref4... ref2hash4: 4 wallclock secs ( 3.58 usr + 0.00 sys = 3.58 CPU) @ 27 +93.30/s (n=10000) shortref4: 2 wallclock secs ( 2.16 usr + 0.00 sys = 2.16 CPU) @ 46 +29.63/s (n=10000) -------------------- Rate ref2hash4 shortref4 ref2hash4 2793/s -- -40% shortref4 4630/s 66% -- ---------------------------------------- Benchmark: timing 2000 iterations of HoH, refHoH... HoH: 16 wallclock secs (10.01 usr + 0.00 sys = 10.01 CPU) @ 19 +9.80/s (n=2000) refHoH: 9 wallclock secs ( 8.98 usr + 0.00 sys = 8.98 CPU) @ 22 +2.72/s (n=2000) -------------------- Rate HoH refHoH HoH 200/s -- -10% refHoH 223/s 11% -- ---------------------------------------- Benchmark: timing 100 iterations of HoHoH, refHoHoH... HoHoH: 30 wallclock secs (28.75 usr + 0.01 sys = 28.76 CPU) @ 3 +.48/s (n=100) refHoHoH: 24 wallclock secs (22.49 usr + 0.00 sys = 22.49 CPU) @ 4 +.45/s (n=100) -------------------- Rate HoHoH refHoHoH HoHoH 3.48/s -- -22% refHoHoH 4.45/s 28% -- ----------------------------------------


-Lee

"To be civilized is to deny one's nature."