This is pretty bad micro-optimizing, but I'm just curious. It usually doesn't make a difference in the "real" world.
So I'm compiling a function (that does nothing) using compile()
then calling exec
on that code and getting a reference to the function I compiled. Then I'm executing it a couple million times and timing it. Then repeating it with a local function. Why is the dynamically compiled function around 15% slower (on python 2.7.2) for just the call?
import datetime
def getCompiledFunc():
cc = compile("def aa():pass", '<string>', 'exec')
dd = {}
exec cc in dd
return dd.get('aa')
compiledFunc = getCompiledFunc()
def localFunc():pass
def testCall(f):
st = datetime.datetime.now()
for x in xrange(10000000): f()
et = datetime.datetime.now()
return (et-st).total_seconds()
for x in xrange(10):
lt = testCall(localFunc)
ct = testCall(compiledFunc)
print "%s %s %s%% slower" % (lt, ct, int(100.0*(ct-lt)/lt))
The output I'm getting is something like:
1.139 1.319 15% slower
Also from what I gather, the reason function calls are so slow is because python needs to look up to make sure the function still exists before it can run it or something? Isn't there any way to just tell it to like...
Using built-in functions and libraries The reason these built-in functions are fast is that python's built-in functions, such as min, max, all, map, etc., are implemented in the C language. You should use these built-in functions instead of writing manual functions that will help you execute your code faster.
The dis.dis() function shows that the code object for each version is identical:
aa
1 0 LOAD_CONST 0 (None)
3 RETURN_VALUE
localFunc
10 0 LOAD_CONST 0 (None)
3 RETURN_VALUE
So the difference is in the function object. I compared each of the fields (func_doc, func_closure, etc) and the one that is different is func_globals. In other words, localFunc.func_globals != compiledFunc.func_globals
.
There is a cost for supplying your own dictionary instead of the built-in globals (the former has to be looked up when a stack frame is created on each call and the latter can be referenced directly by the C code which already knows about the default builtin globals dictionary).
This is easy verified by changing the exec line in your code to:
exec cc in globals(), dd
With that change, the timing difference goes away.
Mystery solved!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With