In C, the following code:
gets compiled very efficiently with optimizations turned on, due to a standard optimization called 'common subexpression elimination' (heretofore referred to as 'CSE'). In effect, the compiler does this:// hypothetical struct x if (flag) { x->y->z->a->b[5]->q = 8; x->y->z->a->b[5]->r = 9; }
Thus, the compiler only has to perform all these dereferences *once*. Unfortunately, unlike C, Perl has to contend with something a little crazier: Tied hashes and arrays. The nature of tied hashes and arrays makes certain things impossible, including CSE. To illustrate my point, I created a benchmark:if (flag) { t = x->y->z->a->b[5]; t->q = 8; t->r = 9; }
The results on my machine are as such:#!/usr/bin/perl use strict; use warnings; use Time::HiRes; use Benchmark qw(cmpthese :hireswallclock); my @bogus; $bogus[1]{foo}[2]{bar}[3]{baz} = 5; $bogus[1]{foo}[2]{bar}[3]{quux} = 6; our @bogus2; $bogus2[1]{foo}[2]{bar}[3]{baz} = 5; $bogus2[1]{foo}[2]{bar}[3]{quux} = 6; cmpthese(1_000_000, { without_common => sub { my $prod = $bogus[1]{foo}[2]{bar}[3]{baz} * $bogus[1]{foo}[2]{bar}[3]{quux}; }, with_common => sub { my $common = $bogus[1]{foo}[2]{bar}[3]; my $prod = $common->{baz} * $common->{quux}; }, without_common_g => sub { my $prod = $bogus2[1]{foo}[2]{bar}[3]{baz} * $bogus2[1]{foo}[2]{bar}[3]{quux}; }, with_common_g => sub { my $common = $bogus2[1]{foo}[2]{bar}[3]; my $prod = $common->{baz} * $common->{quux}; }, });
Note carefully that with_common runs 24% faster than without_common. That's because, even with the extra variable, the three array index and two hash key lookups saved in with_common more than compensate for the extra variable assignment.Rate without_common_g without_common with_common_ +g with_common without_common_g 341880/s -- -4% -20 +% -23% without_common 357910/s 5% -- -16 +% -19% with_common_g 428449/s 25% 20% - +- -3% with_common 443853/s 30% 24% 4 +% --
Also note the 4-5% difference between with_common/without_common and their _g equivalents. This is *entirely* due to the extra hash lookup required to get the address of the global 'bogus2'. This hash lookup is ALWAYS required due to dynamic scoping requirements.
Morals of the story:
$"=$,,$_=q>|\p4<6 8p<M/_|<('=> .q>.<4-KI<l|2$<6%s!<qn#F<>;$, .=pack'N*',"@{[unpack'C*',$_] }"for split/</;$_=$,,y[A-Z a-z] {}cd;print lc
In reply to Perl and common subexpressions by Stevie-O
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |