I use numpy.random.normal function in a tough loop in a class.
class MyClass(MyBaseClass):
def run(self):
while True:
...
self.L.append(numpy.random.normal(0,1))
I know that it’s pretty slow in Python to use multiple lookups. In numpy.random.normal there are 3 lookups: first numpy is looked up, then random, and then normal.
So I decided to address this problem by assigning numpy.random.normal to a local variable _normal.
Here we go:
class MyClass(MyBaseClass):
_normal = numpy.random.normal
def run(self):
while True:
...
self.L.append(MyClass._normal(0,1))
What I’m really concerned about is descriptors. When a variable in a class in accessed, all the bases classes are looked up for the data descriptor with the same name. It’s described here:
Check
objectname.__class__.__dict__for attrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases ofobjectname.__class__for the same case.
So, I guess, if I put _normal in the local space as I did above, it will case looking up all bases classes for the data descriptor. And I wary of it becoming a source of a slowdown.
Are my concerns justified?
Should I worry about the time it takes to look up for the descriptors in base classes?
And is there a better way to speed up access to a function located deep into a module when it’s used in a class?
There was a discussion in the comments to the answers.
I decided to give some additional details of implementation that appeared to be important (for my particular case).
Actually, the code is closer to this (it’s very very simplified):
class MyClass(MyBaseClass):
def __iter__(self):
return self
def next(self):
self.L.append(numpy.random.normal(0,1))
def run(self):
while True:
self.next()
If you must do something like this (is function lookup actually the dominant cost? Random number generation is not cheap) you should realize that one global + one attr lookup (
MyClass._normal) is not that much cheaper than one global + three attr lookups (numpy.random.normal). What you really want is to get zero global or attr lookups inside the loop, which you can only do by defining_normalinside the function. If you’re really desperate to shave cycles you should also prebind the list append call:Contrast disassembly output (just for the
appendstatement):vs
What would be even better is to vectorize — generate many random normal deviates from and append them to the list in one go — you can do that with the
sizeargument tonumpy.random.normal.