【发布时间】:2017-10-22 21:12:18
【问题描述】:
考虑:
Code A
def foo():
pass
for i in range(1000000):
foo()
Code B
for i in range(1000000):
def foo():
pass
foo()
两个代码 sn-ps 之间的唯一区别是 foo 在每次迭代时在循环内不断重新定义。
运行一些基准测试:
Code A
10 loops, best of 3: 102 ms per loop
Code B
10 loops, best of 3: 188 ms per loop
因此,不断地重新定义函数是不必要的开销。
Code B 的字节码如下所示:
1 0 SETUP_LOOP 39 (to 42)
3 LOAD_NAME 0 (range)
6 LOAD_CONST 0 (1000000)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 GET_ITER
>> 13 FOR_ITER 25 (to 41)
16 STORE_NAME 1 (i)
2 19 LOAD_CONST 1 (<code object foo at 0x103113390, file "<dis>", line 2>)
22 LOAD_CONST 2 ('foo')
25 MAKE_FUNCTION 0
28 STORE_NAME 2 (foo)
4 31 LOAD_NAME 2 (foo)
34 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
37 POP_TOP
38 JUMP_ABSOLUTE 13
>> 41 POP_BLOCK
>> 42 LOAD_CONST 3 (None)
45 RETURN_VALUE
如您所见,函数定义并未在循环外进行优化(见25 MAKE_FUNCTION 行)。
这看起来很简单,将函数创建移出循环,因为它的声明显然不是循环执行的条件。
是否有任何明显的障碍阻止这样做?
【问题讨论】:
-
Python 通常做的优化很少。
-
(你可以很容易地问为什么它没有优化整个循环。)
-
你试过Pypy,一个即时编译器吗?
-
如果你能证明生成的代码块 never 会有所不同,并且 never 会依赖
i在范围内,那么我想您可以将其移出...但是,在那一点上,您可以证明它从未改变过,您已经必须通过整个循环来做到这一点...这可能只是基于输入的本次运行是一致的下次运行可能不成立。 -
因为在解释器中,与编译代码相反,为时已晚。你已经进入了循环。由于动态对象(类型!)的创建,python 通常无法在代码实际运行之前跨越范围边界。
标签: python function optimization