简体   繁体   中英

Reduce function calls

I profiled my python program and found that the following function was taking too long to run. Perhaps, I can use a different algorithm and make it run faster. However, I have read that I can also possibly increase the speed by reducing function calls, especially when it gets called repeatedly within a loop. I am a python newbie and would like to learn how to do this and see how much faster it can get. Currently, the function is:

def potentialActualBuyers(setOfPeople,theCar,price):
    count=0
 for person in setOfPeople:
  if person.getUtility(theCar) >= price and person.periodCarPurchased==None:
   count += 1
 return count

where setOfPeople is a list of person objects. I tried the following:

    def potentialActualBuyers(setOfPeople,theCar,price):
        count=0
        Utility=person.getUtility
        for person in setOfPeople:
           if Utility(theCar) >= price and person.periodCarPurchased==None:
               count += 1
        return count

This, however, gives me an error saying local variable 'person' referenced before assignment Any suggestions, how I can reduce function calls or any other changes that can make the code faster.

Again, I am a python newbie and even though I may possibly be able to use a better algorithm, it is still worthwhile learning the answer to the above question.

Thanks very much.

***** EDIT *****

Adding the getUtility method:

 def getUtility(self,theCar):
  if theCar in self.utility.keys():
   return self.utility[theCar]
  else:
   self.utility[theCar]=self.A*(math.pow(theCar.mpg,self.alpha))*(math.pow(theCar.hp,self.beta))*(math.pow(theCar.pc,self.gamma))

return self.utility[theCar]

***** EDIT: asking for new ideas *****

Any ideas how to speed this up further. I used the method suggested by Alex to cut the time in half. Can I speed this further? Thanks.

I doubt you can get much speedup in this case by hoisting the lookup of person.getUtility (by class, not by instances, as other instances have pointed out). Maybe...:

return sum(1 for p in setOfPeople
           if p.periodCarPurchased is None
           and p.getUtility(theCar) >= price)

but I suspect most of the time is actually spent in the execution of getUtility (and possibly in the lookup of p.periodCarPurchased if that's some fancy property as opposed to a plain old attribute -- I moved the latter before the and just in case it is a plain attribute and can save a number of the getUtility calls). What does your profiling say wrt the fraction of time spent in this function (net of its calls to others) vs the method (and possibly property) in question?

Try instead (that's assuming all persons are of the same type Person ):

Utility = Person.getUtility
for person in setOfPeople:
    if Utility (person, theCar) >= ...

Also, instead of == None using is None should be marginally faster. Try if swapping and terms helps.

Methods are just functions bound to an object:

    Utility = Person.getUtility
    for person in setOfPeople:
       if Utility(person, theCar) ...

This doesn't eliminate a function call though, it eliminates an attribute lookup.

This one line made my eyes bleed:

   self.utility[theCar]=self.A*(math.pow(theCar.mpg,self.alpha))*(math.pow(theCar.hp,self.beta))*(math.pow(theCar.pc,self.gamma))

Let's make it legible and PEP8able and then see if it can be faster. First some spaces:

self.utility[theCar] = self.A * (math.pow(theCar.mpg, self.alpha)) * (math.pow(theCar.hp, self.beta)) * (math.pow(theCar.pc, self.gamma))

Now we can see there are very redundant parentheses; remove them:

self.utility[theCar] = self.A * math.pow(theCar.mpg, self.alpha) * math.pow(theCar.hp, self.beta) * math.pow(theCar.pc, self.gamma)

Hmmm: 3 lookups of math.pow and 3 function calls. You have three choices for powers: x ** y , the built-in pow(x, y[, z]) , and math.pow(x, y) . Unless you have good reason for using one of the others, it's best (IMHO) to choose x ** y ; you save both the attribute lookup and the function call.

self.utility[theCar] = self.A * theCar.mpg ** self.alpha * theCar.hp ** self.beta * theCar.pc ** self.gamma

annnnnnd while we're here, let's get rid of the horizontal scroll-bar:

self.utility[theCar] = (self.A
    * theCar.mpg ** self.alpha
    * theCar.hp  ** self.beta
    * theCar.pc  ** self.gamma)

A possibility that would require quite a rewrite of your existing code and may not help anyway (in Python) would be to avoid most of the power calculations by taking logs everywhere and working with log_utility = log_A + log_mpg * alpha ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM