为什么在python中的空函数调用对于动态编译的python代码来说要慢15%左右

Ami*_*mir 7 python optimization dynamic micro-optimization python-2.7

这是非常糟糕的微优化,但我只是好奇.它通常不会对"真实"世界产生影响.

所以我正在编译一个函数(什么都不做),compile()然后调用exec该代码并获取对我编译的函数的引用.然后我执行了几百万次计时.然后用本地函数重复它.为什么动态编译的函数只需要调用约15%(在python 2.7.2上)?

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))
Run Code Online (Sandbox Code Playgroud)

我得到的输出是这样的:

1.139 1.319 15% slower
Run Code Online (Sandbox Code Playgroud)

Ray*_*ger 11

()dis.dis函数示出了对于每一个版本的代码对象是相同的:

aa
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
localFunc
 10           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE 
Run Code Online (Sandbox Code Playgroud)

所以区别在于函数对象.我比较了每个字段(func_doc,func_closure等),而不同的字段是func_globals.换句话说,localFunc.func_globals != compiledFunc.func_globals.

提供自己的字典而不是内置全局字符需要付出代价(前者必须在每次调用时创建堆栈帧时查找,后者可以由已经知道默认值的C代码直接引用内置全局字典).

通过将代码中的exec行更改为:

exec cc in globals(), dd
Run Code Online (Sandbox Code Playgroud)

随着这种变化,时间差异消失了.

谜团已揭开!