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)
随着这种变化,时间差异消失了.
谜团已揭开!