Can you make this faster?

Roy Smith roy at panix.com
Sun Jun 27 15:14:45 EDT 2004


In article <889cbba0.0406271022.fd1f9ac at posting.google.com>,
 klachemin at home.com (Kamilche) wrote:

> I have a routine that I really need, but it slows down processing
> significantly. Can you spot any ineffeciencies in the code?
> 
> This code makes a critical function of mine run about 7x slower than
> using a prebuilt format string. For maximum flexibility, it would be
> best to calculate the format string using this method, so I'd dearly
> love to keep it.
> 
> def fmtstring(args):
>     delim = '\0'
>     fmt = []
>     fmt.append('<')
>     for arg in args:
>         t = type(arg)
>         if t == types.StringType:
>             l = len(arg)
>             fmt.append(str(l) + 's')
>         elif t == types.IntType:
>             fmt.append('i')
>         elif t == types.LongType:
>             fmt.append('q')
>         elif t == types.BooleanType:
>             fmt.append('c')
>         elif t == types.FloatType:
>             fmt.append('d')
>         else:
>             raise Exception("Can't pack argument of type %s!" % t)
>     s = ''.join(fmt)
>     s = s + '\0'
>     return s

The first thing is to profile your code and make sure this really is 
significant in the overall scheme of things.  From your description, it 
sounds like you've already done that, so I'll go with that assumption.

What's the most likely value for type(arg)?  If 90% of your args are 
floats, for example, most of the time you work your way all the way down 
the if/else tree before getting to the right spot.  Putting the tests in 
order of probability of occurance will help you a little.

Doing "from types import *" and then being able to just say "IntType" 
instead of "types.IntType" will be a little faster since there will be 
one less name lookup.

Another thing you can try is doing dictionary lookups instead of an 
if/else tree.  Something like:

map = {IntType: 'i',
       LongType: 'q',
       BooleanType: 'c',
       FloatType: 'd'}
[...]
try:
   fmt.append (map[t])
except KeyError:
   raise Exception ("Can't pack %s" % t)

You still need to special-case StringType, however.

Lastly, is an idea which is really quite gross.  If you're desperate for 
speed, the majority of your args are strings, and you have no pride, it 
might be worth trying, however.  Instead of saying:

t = type(arg)
if t == types.StringType:
    l = len(arg)

you could do something like:

try:
   l = len (arg)
   fmt.append(str(l) + 's')
except TypeError:
   process all the non-string types

The idea here is to not waste time getting and testing the type, just 
assume it's a string and deal with the consequences later if your 
assumption was wrong.  In real life, this plays out as, "It's easier to 
ask forgiveness than permission".

Of the types you mentioned, string is the only one which which has a 
length; all the others will raise TypeError when passed to len().  
Exception processing is relatively slow, but if it only happens a small 
percentage of the time, you might be ahead of the game this way.

Some people would call this elegant, others would call it a gross hack.  
I guess it depends on your perspective, and how desperate you are to 
make your code run faster :-)

Of course, you could put several of these ideas together.  Try the 
exception-catching idea for strings first, and then sort out the other 4 
types using a dictionary lookup.

My gut feeling is that even with all these tricks, the best you'll be 
seeing is a factor of 2 or 3 speedup.  I'd be amazed if you could get 
anywhere near the 7x you're looking for.



More information about the Python-list mailing list