Tuning a python script
Jul. 15th, 2008 08:58 pm
Sure enough, when I profiled the base version using a fixed configuration, I found that it was losing most of its time to a series of deepcopy() calls. I changed the internal data structures to use a dictionary instead of a more complex set of arrays and, presto, the run time went down from 7.55s to 1.76s and the function call count went down from 398,359 to 53,678.
But when I re-profiled, I found a couple of other nasties: an N by N array that was being needlessly regenerated for every loop trip and a whole host of calls to has_key(). After adding caching to the main loop to prevent recalculation and replacing all the has_keys() with try ... execpt KeyError ... constructs, I managed to get the time down to 0.97s and a mere 11,236 function calls.
And here's proof of my success: a plot showing how the raw and optimised versions of the program scale as the number of items increases:
So it looks like the big wins were, in order of effectiveness: (a) to use a more efficient algorithm and data structures; (b) to replace has_keys() with exceptions in order to avoid the overhead of the extra subroutine calls and to exploit the (relatively) fast performance of exception handling; (c) to avoid unnecessary work by caching results, trading memory for CPU. Of these, (a) and (c) were obvious, while (b) was only apparent after a little bit of experimentation with the profiler.
no subject
Date: 2008-07-16 10:24 am (UTC)no subject
Date: 2008-07-16 08:32 pm (UTC)I can't say I'll be sorry to see has_key() go. I only ever seem to use it on occasions when I'm forced to switch back and forth between python and perl, so it's probably a cargo cult carry over from perl...