What a great example Tilly! This really got my imagination going. When I first looked at your line of code:
print join ",", map { &{$field_info{$_}{format}}($deal) } @fields;
I had no idea where to begin. I couldn't see anything wrong at first. Thankfully, you're post, and the subject at hand, gave enough information that I could infer/guess what the other data structures might look like. In order to optimize your code, I had to imagine & construct the code that wraps around it, in order to simulate it's environment.
After doing some simple code, I set to look at optimizations. Right away nothing jumped out at me. I thought about hash slices, not using map, using the arrow operator, replacing join with $, and more; some of them worked, others didn't.
Then, I remembered that we are looping over a set of deals. So I made an @deals array, and started to loop over it. Then I saw it. The map was taking the @fields, and was
dereferencing the subref's each time through the @deals loop. Same thing each time through. Now, assuming that @fields is not modified in the loop, we can factor it out, and do it a single time outside of the @deals loop.
There are more optimizations I could make to my code, but I didn't want to stray too far from attempting to optimize Tilly's code, and loose the point of the excercise.
Anyhow, this is what I came up with:
#!/usr/bin/perl -w
use strict;
#I am sure there are more fields than this
my @fields = qw(a b);
#Subroutines that return a value. I imagine
#you had alot more processing in here, but
#this is all make-believe =)
my %field_info = (
a => { format => sub {return $_[0]->{deep}{inside}{a}} },
b => { format => sub {return $_[0]->{deep}{inside}{b}} },
);
#Had to simulate a reasonable amount of data, that could
#potentially cause a slowdown
#Let's make @ deals
my @deals;
for(my $i = 0; $i < 10000; $i++) {
push @deals, { deep => { inside => { a => "a[$i]", b => "b[$i]"} } }
+;
}
#Force commas to be the field seperator inside
#print(), so we can drop the join(), and pass an array
#to print, rather than one long "joined" string - it should be faster
local $, = ',';
#Set the output record seperator, so that each call to
#print tags on a "\n" after printing out the $deal
local $\ = "\n";
#Dereference the subroutines, so that we aren't doing a hash traversal
#each time through the @deals loop AND the map.
my @dereferenced_subs = map { $_->{format} } @field_info{ @fields };
#Print out each $deal
foreach my $deal (@deals) {
print map { $_->($deal) } @dereferenced_subs;
}
How's that, did I miss anything?
Update: I found one optimization, although it could be at the expense of readability. The part where I dereference the subs before going into a loop, I changed to a hash slice to improve the speed of the hash lookup, if there was alot of fields this could make a larger difference. Also, I added a \n after each print, so that the information could be pretty printed. |