[Tutor] What techniques should I use to make my code run faster? Informational

anil maran anilmrn at yahoo.com
Thu Aug 10 00:16:37 CEST 2006


      What techniques should I use to make my code run faster?         Always profile before you optimize for speed.   You should always optimize for readability first: it's easier to tune   readable code than to read 'optimized' code, especially if the optimizations   are not effective. Before using any technique that makes the code less   readable, you should check that it's actually a bottleneck in your   application by running your application with the built-in profile.py   script. If your program spends 10% of its time running a particular   method, even if you increase its speed tenfold you've only shaved 9% off the   total running time.   
      Always use a good algorithm when it is available.   The exception to the above rule is when there are known large differences in the   time complexity of alternative algorithms. Reducing running time from   quadratic to linear, or from exponential to polynomial, is always worth   doing unless you are sure that the data sets will always be tiny (less than   a couple of dozen items).   
       Use the simplest option that could possibly work.   Don't use a regular expression if you just want to see if a string starts   with a particular substring: use .startswith instead. Don't use   .index if you just want to see if a string contains a particular   letter: use in instead. Don't use StringIO if you could   just use a list of strings. In general, keeping it simple cuts down on   bugs and makes your code more readable. Even a complicated combination of   .index calls will be much faster than a regular expression, and   probably easier to decipher if you're just matching rather than capturing   the result.   
      Build strings as a list and use ''.join at the end.   Yes, you already saw this one above under "Python Idioms", but it's such   an important one that I thought I'd mention it again.   join is a string method called on the separator,   not the list. Calling it from the empty string concatenates the pieces   with no separator, which is a Python quirk and rather surprising at first.   This is important: string building with + is quadratic time instead of    linear!   
   
Wrong: 
for s in strings: result += s
Right: 
result = ''.join(strings)
  
       Use tests for object identity when appropriate: if x is not    None rather than if x != None. It is much more efficient to   test objects for identity than equality, because identity only checks   their address in memory (two objects are identical if they are the same   object in the same physical location) and not their actual data.   
       Use dictionaries for searching, not lists. To find items in common   between two lists, make the first into a dictionary and then look for items   in the second in it. Searching a list for an item is linear-time, while   searching a dict for an item is constant time. This can often let you   reduce search time from quadratic to linear.   
       Use the built-in sort wherever possible. sort can   take a custom comparison function as a parameter, but this makes it very   slow because the function has to be called at least O(n log n) times in the   inner loop. To save time, turn the list of items into a list of tuples,   where the first element of each tuple has the precalculated value of the   function for each item (e.g. extracting a field), and the last element is the   item itself.   
      This idiom is called DSU for 'decorate-sort-undecorate.'   In the 'decorate' step, make a list of tuples   containing (transformed_value, second_key, ... , original value).   In the 'sort' step, use the built-in sort on the tuples. In the   'undecorate' step, retrieve the original list in the sorted order by   extracting the last item from each tuple. For example:   
   
aux_list = [i.Count, i.Name, ... i) for i in items]
aux_list.sort()    #sorts by Count, then Name, ... , then by item itself
sorted_list = [i[-1] for i in items] #extracts last item
  
       Use map and/or filter to apply functions to lists.   map applies a function to each item in a list (technically, sequence)   and returns a list of the results. filter applies a function to each   item in a sequence, and returns a list containing only those items for which   the function evaluated True (using the __nonzero__ built-in   method). These functions can make code much shorter. They also make it much   faster, since the loop takes place entirely in the C API and never has to   bind loop variables to Python objects.   
   
Worse:
strings = []
for d in data:
    strings.append(str(d))

Better:
strings = map(str, data)
  
       Use list comprehensions where there are conditions attached, or where    the functions are methods or take more than one parameter. These are   cases where map and filter do badly, since you have to   make up a new one-argument function that does the operation you want. This   makes them much slower, since more work is done in the Python layer. List   comprehensions are often surprisingly readable.   
   
Worse:
result = []
for d in data:
    if d.Count > 4:
        result.append[3*d.Count]

Better:
result = [3*d.Count for d in data if d.Count > 4]
  
      If you find yourself making the same list comprehension repeatedly, make   utility functions and use map and/or filter:   
   
def triple(x):
    """Returns 3 * x.Count: raises AttributeError if .Count missing."""
    return 3 * x.Count

def check_count(x):
    """Returns 1 if x.Count exists and is greater than 3, 0 otherwise."""
    try:
        return x.Count > 3
    except:
        return 0

result = map(triple, filter(check_count, data))
  
       Use function factories to create utility functions.   Often, especially if you're using map and filter a lot,   you need utility functions that convert other functions or methods to   taking a single parameter. In particular, you often want to bind some data   to the function once, and then apply it repeatedly to different objects.   In the above example, we needed a function that multiplied a particular field   of an object by 3, but what we really want is a factory that's able to return   for any field name and amount a multiplier function in that family:   
   
def multiply_by_field(fieldname, multiplier):
    """Returns function that multiplies field "fieldname" by multiplier."""
    def multiplier(x):
        return getattr(x, fieldname) * multiplier
    return multiplier

triple = multiply_by_field('Count', 3)
quadruple = multiply_by_field('Count', 4)
halve_sum = multiply_by_field('Sum', 0.5)
  
      This is a very powerful and general technique for producing functions that   might do something like search a specified field for a list of words, or   perform several actions on different fields of a particular object, etc.   It's a pain to write a lot of little functions that do very similar things,   but if they're produced by a function factory it's easy.   
       Use the operator module and reduce to get sums, products, etc.   reduce takes a function and a sequence. First it applies the   function to the first two items, then it takes the result and applies the   function to the result and the next item, takes that result and applies the   function to it and the next item, and so on until the end of the list. This   makes it very easy to accumulate items along a list (or, in fact, any   sequence). Note that Python 2.3 has a built-in sum() function (for numbers only),   making this less necessary than it used to be.   
   
Worse:
sum = 0
for d in data:
    sum += d
product = 1
for d in data:
    product *= d

Better:
from operator import add, mul
sum = reduce(add, data)
product = reduce(mul, data)
  
       Use zip and dict to map fields to names.   zip turns a pair of sequences into a list of tuples containing   the first, second, etc. values from each sequence. For example,   zip('abc', [1,2,3]) == [('a',1),('b',2),('c',3)]. You can use   this to save a lot of typing when you have fields in a known order that   you want to map to names:   
   
Bad:
fields = '|'.split(line)
gi = fields[0]
accession = fields[1]
description = fields[2]
#etc.
lookup = {}
lookup['GI'] = gi
lookup['Accession'] = accession
lookup['Description'] = description
#etc.

Good:
fieldnames = ['GI', 'Accession', 'Description'] #etc.
fields = '|'.split(line)
lookup = dict(zip(fieldnames, fields))

Ideal:
def FieldWrapper(fieldnames, delimiter, constructor=dict):
    """Returns function that splits a line and wraps it into an object.

    Field names are passed in as keyword args, so constructor must be
    expecting them as such.
    """
    def FieldsToObject(line):
        fields = [field.strip() for field in line.split(delimiter)]
        result = constructor(**dict(zip(fieldnames, fields)))
    return FieldsToObject

FastaFactory = FieldWrapper(['GI','Accession','Description'], '|', Fasta)
TaxonFactory = FieldWrapper(['TaxonID', 'ParentID', ...], '|', Taxon)
CodonFreqFactory = FieldWrapper(['UUU', 'UUC', 'UUA',...], ' ', CodonFreq)
#etc for similar data, including any database tables you care to wrap
  

 		
---------------------------------
Talk is cheap. Use Yahoo! Messenger to make PC-to-Phone calls.  Great rates starting at 1¢/min.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.python.org/pipermail/tutor/attachments/20060809/16a5be90/attachment-0001.html 


More information about the Tutor mailing list