Tim Peters解释了为什么这很慢,但是让我们看看如何改进它。
坚持你的sum(range(...))
示例(因为这个示例比你的示例小10倍,可以适合在这里内存中使用):
import numpy
import array
L = list(range(10**7))
A = array.array('l', L)
N = numpy.array(L)
%timeit sum(L)
10 loops, best of 3: 101 ms per loop
%timeit sum(A)
1 loop, best of 3: 237 ms per loop
%timeit sum(N)
1 loop, best of 3: 743 ms per loop
这种方式还需要对numpy进行装箱/拆箱,这会增加额外的开销。要使其快速,必须在numpy c代码内部保持:
%timeit N.sum()
100 loops, best of 3: 6.27 ms per loop
从链表方案到numpy版本,运行时间提升了16倍。
让我们也检查一下创建这些数据结构需要多长时间。
%timeit list(range(10**7))
1 loop, best of 3: 283 ms per loop
%timeit array.array('l', range(10**7))
1 loop, best of 3: 884 ms per loop
%timeit numpy.array(range(10**7))
1 loop, best of 3: 1.49 s per loop
%timeit numpy.arange(10**7)
10 loops, best of 3: 21.7 ms per loop
明显的赢家:Numpy
还要注意的是,创建数据结构所需的时间与求和一样多,甚至更多。分配内存很慢。
它们的内存使用情况:
sys.getsizeof(L)
90000112
sys.getsizeof(A)
81940352
sys.getsizeof(N)
80000096
所以,这些数字每个需要8个字节,其中包含一些额外的开销。对于我们使用的范围,32位整数已足够,因此我们可以节省一些内存。
N=numpy.arange(10**7, dtype=numpy.int32)
sys.getsizeof(N)
40000096
%timeit N.sum()
100 loops, best of 3: 8.35 ms per loop
但事实证明,在我的计算机上,添加64位整数比32位整数更快,因此只有在受到内存/带宽限制时才值得这样做。
array
在将表示ASCII字节的整数字符串转换为str
对象时非常快速。即使是Guido自己也经过了许多其他解决方案后才想出这个方法,并对其性能感到相当惊讶。无论如何,在我记忆中只有这里看到它被用到了。处理数组问题上,numpy
更好,但它是第三方依赖项。 - Bakuriu