I recently finished reading the new addition of "Effective Perl Programming: Ways to Write Better, More Idiomatic Perl", 2ed, by Joseph Hall, Joshua McAdama, and brian d foy. It is a throuhly delightful read (see my review in the "Reviews" section of the Perl Monks site) and I have begun going back through it to explore the multitude of good examples. I have run upon a curous behavior that seems to defy the wisdom in one place in the text.
The example is drawn from Chapter 4: Subroutines on Item 46: Pass references instead of copies. The example is as follows:
Passing Copies:
sub sum { my @numbers = @_; my $sum = 0; foreach my $num (@numbers){ $sum += $num } $sum; } print sum( 1 .. 100_000 ),"\m";
Versus a version that passes references, a la:
sub sum { my ($numbers_ref) = @_; my $sum = 0; foreach my $num (@$numbers_ref){ $sum += $num }; $sum; } print sum([1..100_000]), "\n";
In the text the authors state that in the firs case "Perl has to copy 100,000 elements in @numbers in sum, although it doesn't change any of the values. Perl does a lot of work for no good reason...A small change in the subroutine fixes that." And that results in the second examle of sum.
So I coded each up and decided to see just how much difference it made. I used the module Time::HiRes using the following code:
#!/user/bin/perl use strict; use warnings; use Time::HiRes qw(gettimeofday tv_interval); sub sum { my ($numbers_ref) = @_; my $sum = 0; foreach my $num (@$numbers_ref){ $sum += $num }; return $sum; } # end sub sum() my $t0 = [gettimeofday]; print sum([1..100_000]); print "\n"; my $t1 = [gettimeofday]; my $elapsed = tv_interval($t0, $t1); print "\n"; print "Elapsed time: $elapsed seconds\n"; exit(0);
and got the following results:
First version of sub(): 0.027007 seconds Second version of sub(): 0.024075 seconds
So far, so good...exactly as the authors contended...not as dramatic a difference as I had imagined, but clearly consistent with the authors' position.
Ever the curious type, I decided to try a more dramatic case...I decided to try it with 1,000,000 numbers. When I did, I got the following unexpected result:
First version of sum(): 0.09366 seconds Second version of sum(): 0.372829 seconds
The first part of the result seems logical to me since it uses more numbers and is appropriately greater (timewise) than the first test cases offered by the authors. But the performace of the second version of my 1,000,000 number case is most curious, unexpected and inexplicable to me: the pass-by-ref case takes almost 40 times LONGER than the pass-by-copies case! Anyone got some insight into why that happens?
For reference I am running on Windows XP (Version 2002 Service Pack 3) on an Intel Core 2 CPU 6300 running @ 1.86 GHz with 3.49 GBytes of 1.86 GHz RAM. I am using ActiveStates v5.8.8 Build 820 Perl.
UPDATE: Thanks everyone for the responses. Seems I wasn't timing what I thought I was timing. Thanks especilly to linuxer and BrowserUk for your help. I was inadvertantly timing the creation of the references rather than how long it took to process with and without them. Their results restored the truth and are consistent with what the authors' thesis was. Thanks, again, for the help.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Curious Perl Behavior...
by BrowserUk (Patriarch) on May 25, 2010 at 21:51 UTC | |
by ack (Deacon) on May 26, 2010 at 15:23 UTC | |
|
Re: Curious Perl Behavior...
by linuxer (Curate) on May 25, 2010 at 21:41 UTC | |
by ack (Deacon) on May 26, 2010 at 15:17 UTC | |
|
Re: Curious Perl Behavior...
by repellent (Priest) on May 25, 2010 at 21:33 UTC | |
by ack (Deacon) on May 26, 2010 at 15:09 UTC | |
|
Re: Curious Perl Behavior...
by deMize (Monk) on May 28, 2010 at 19:13 UTC | |
by BrowserUk (Patriarch) on May 28, 2010 at 19:59 UTC |