我注意到 Python 的内置 sum
函数在对 1 000 000 个整数的列表求和时大约比 for 循环快 3 倍:
import timeit
def sum1():
s = 0
for i in range(1000000):
s += i
return s
def sum2():
return sum(range(1000000))
print 'For Loop Sum:', timeit.timeit(sum1, number=10)
print 'Built-in Sum:', timeit.timeit(sum2, number=10)
# Prints:
# For Loop Sum: 0.751425027847
# Built-in Sum: 0.266746997833
这是为什么呢? sum
是如何实现的?
最佳答案
速度差异实际上大于 3 倍,但是您可以通过首先创建一个包含 100 万个整数的巨大内存列表来降低任一版本的速度。将其与时间试验分开:
>>> import timeit
>>> def sum1(lst):
... s = 0
... for i in lst:
... s += i
... return s
...
>>> def sum2(lst):
... return sum(lst)
...
>>> values = range(1000000)
>>> timeit.timeit('f(lst)', 'from __main__ import sum1 as f, values as lst', number=100)
3.457869052886963
>>> timeit.timeit('f(lst)', 'from __main__ import sum2 as f, values as lst', number=100)
0.6696369647979736
现在速度差已经上升到5倍以上。
for
循环作为解释的 Python 字节码执行。 sum()
完全在 C 代码中循环。解释字节码和C代码的速度差异很大。
此外,如果 C 代码可以将总和保存在 C 类型中,则它确保不会创建新的 Python 对象;这适用于 int
和 float
结果。
反汇编的 Python 版本是这样的:
>>> import dis
>>> def sum1():
... s = 0
... for i in range(1000000):
... s += i
... return s
...
>>> dis.dis(sum1)
2 0 LOAD_CONST 1 (0)
3 STORE_FAST 0 (s)
3 6 SETUP_LOOP 30 (to 39)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 2 (1000000)
15 CALL_FUNCTION 1
18 GET_ITER
>> 19 FOR_ITER 16 (to 38)
22 STORE_FAST 1 (i)
4 25 LOAD_FAST 0 (s)
28 LOAD_FAST 1 (i)
31 INPLACE_ADD
32 STORE_FAST 0 (s)
35 JUMP_ABSOLUTE 19
>> 38 POP_BLOCK
5 >> 39 LOAD_FAST 0 (s)
42 RETURN_VALUE
除了解释器循环比 C 慢之外,INPLACE_ADD
将创建一个新的整数对象(超过 255,CPython 将小的 int
对象缓存为单例)。
你可以看到 C implementation在 Python mercurial 代码存储库中,但它在注释中明确指出:
/* Fast addition by keeping temporary sums in C instead of new Python objects.
Assumes all inputs are the same type. If the assumption fails, default
to the more general routine.
*/
关于Python 内置求和函数 vs. for 循环性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24578896/