Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is an empty function call in python around 15% slower for dynamically compiled python code

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
like image 886
Amir Avatar asked Nov 19 '11 02:11

Amir


People also ask

Why are Python function calls slow?

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...

Do functions make code run faster Python?

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.


1 Answers

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!

like image 170
Raymond Hettinger Avatar answered Oct 20 '22 00:10

Raymond Hettinger