我发现python的VM似乎对CPU-Cache非常不友好。
例如:import time
a = range(500)
sum(a)
for i in range(1000000): #just to create a time interval, seems this disturb cpu cache?
pass
st = time.time()
sum(a)
print (time.time() - st)*1e6
:
> 100us
的另一个例子:
import time
a = range(500)
for i in range(100000):
st = time.time()
sum(a)
print (time.time() - st)*1e6
:
~ 20us
我们可以看到,当频繁运行时,代码变得快得多。
有解决方案吗?
我们可以参考这个问题:(应该是同一个问题)Python函数(或代码块)在循环
中设置一个时间间隔,运行速度会慢得多。我觉得这个问题很难。必须对python虚拟机、c语言和cpu缓存的原理有深入的了解。你有什么建议关于在哪里张贴这个问题可能的答案?
你的假设"我们可以看到当频繁运行时,代码变得更快"是不太正确的。
这个循环
#just to create a time interval, seems this disturb cpu cache?
for i in range(1000000):
pass
不仅仅是一个简单的时间延迟。在Python 2中,它创建了一个包含1,000,000个整数对象的列表,循环遍历它,然后销毁它。在Python 3中,range()
是一个生成器,所以它对内存的浪费要少得多;Python 2对应的是xrange()
。
在标准CPython实现中,列表是指向其所包含对象的指针数组。在32位系统中,整数对象占用12个字节,空列表对象占用32个字节,当然指针占用4个字节。所以range(1000000)
总共消耗32 + 1000000 *(4 + 12)= 16000032字节。
一旦循环结束,range()
列表上的引用计数降为零,因此它及其内容有资格进行垃圾收集。垃圾收集一百万个整数需要花费一点时间,所以在循环结束后注意到时间延迟并不奇怪。此外,创建&释放所有这些对象通常会导致一些内存碎片,这将影响后续代码的效率。
FWIW,在我的2GHz Pentium 4 Linux系统上,您的第一个代码块在38 - 41µs范围内打印值,但如果我将环路更改为使用xrange()
,则时间下降到31 - 34µs。
如果代码被修改为在计算sum(a)
之前防止range()
的垃圾收集,那么我们使用range()
还是xrange()
在计时上没有什么区别。
import time
a = range(500)
sum(a)
bigrange = xrange(1000000)
for i in bigrange:
pass
st = time.time()
sum(a)
print (time.time() - st) * 1e6
#Maintain a reference to bigrange to prevent it
#being garbage collected earlier
print sum(bigrange)