【发布时间】:2020-05-17 04:57:28
【问题描述】:
(更新:可能只发生在适用于 Windows 的 CPython 3.8 32 位中,因此如果您无法在其他版本中重现它,请不要感到惊讶。请参阅更新部分中的表格。)
iter 和 reversed 都会生成列表的专用迭代器:
>>> iter([1, 2, 3])
<list_iterator object at 0x031495C8>
>>> reversed([1, 2, 3])
<list_reverseiterator object at 0x03168310>
但reversed 的速度要慢得多:
> python -m timeit -s "a = list(range(1000))" "list(iter(a))"
50000 loops, best of 5: 5.76 usec per loop
> python -m timeit -s "a = list(range(1000))" "list(reversed(a))"
20000 loops, best of 5: 14.2 usec per loop
而且我可以始终如一地重现它。后来我又试了五次iter,分别是5.98、5.84、5.85、5.87、5.86。然后reversed又五次,分别用了14.3、14.4、14.4、14.5、14.3。
我认为iter 可能受益于增加列表元素的内存位置,因此我尝试事先反转列表。同一张图:
> python -m timeit -s "a = list(range(1000)); a.reverse()" "list(iter(a))"
50000 loops, best of 5: 5.73 usec per loop
> python -m timeit -s "a = list(range(1000)); a.reverse()" "list(reversed(a))"
20000 loops, best of 5: 14.1 usec per loop
与sum 相同的图片:
> python -m timeit -s "a = list(range(1000))" "sum(iter(a))"
20000 loops, best of 5: 10.7 usec per loop
> python -m timeit -s "a = list(range(1000))" "sum(reversed(a))"
10000 loops, best of 5: 20.9 usec per loop
还有相同的元素:
> python -m timeit -s "a = [None] * 1000" "list(iter(a))"
50000 loops, best of 5: 6.35 usec per loop
> python -m timeit -s "a = [None] * 1000" "list(reversed(a))"
20000 loops, best of 5: 14.5 usec per loop
为什么反向迭代器这么慢?
我在带有 Intel i5-7200U(它是 HUAWEI MateBook X)的 Windows 10 pro 64 位版本 1903 上使用 CPython 3.8.1 32 位。无需特殊配置,只需在普通 Windows 安装上进行普通 Python 安装即可。
更新: 我在另一台机器(Pentium N3700、Windows 10 Pro 64-bit 1903)上运行了一个更大的自动化测试,其中包含八个不同的 Python 版本(所有新安装的默认设置)。每个循环的 usec 时间:
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 16.6 17.0 15.2 16.2
3.6.8 16.8 17.2 14.9 15.8
3.7.6 16.5 16.9 14.8 15.5
3.8.1 16.3 22.1 14.6 15.5
需要注意的两点:
- Python 3.8.1 32 位
reversed是唯一慢得多的。或许可以解释为什么几乎没有其他人可以复制它。 - 在所有其他七个版本中,
reversed比iter慢一点。 32 位大约 0.4 微秒,64 位大约 0.9 微秒。
我以循环方式运行了这 16 个测试十轮,上面显示的每次都是其十个源时间中最好的一次。 160 个源时间中的每一个都是这样完成的:
python.exe -m timeit -r 5 -s "a = list(range(1000))" "list(iter(a))"
or
python.exe -m timeit -r 5 -s "a = list(range(1000))" "list(reversed(a))"
16 项测试中每一项的时间都非常一致。全表(注意循环意味着我逐列而不是逐行运行这些):
3.5.4 32-bit iter [16.7, 16.6, 17.3, 16.6, 16.7, 16.6, 16.6, 16.6, 16.6, 16.7]
3.5.4 32-bit reversed [17.1, 17.1, 17.1, 17.2, 17.1, 17.1, 17.0, 17.1, 17.1, 17.1]
3.5.4 64-bit iter [15.2, 15.4, 15.4, 15.4, 15.4, 15.4, 15.4, 15.3, 15.4, 15.3]
3.5.4 64-bit reversed [16.8, 16.2, 16.3, 16.3, 16.2, 16.2, 16.2, 16.2, 16.2, 16.3]
3.6.8 32-bit iter [17.3, 16.9, 16.8, 16.9, 16.9, 16.8, 16.9, 16.9, 16.8, 16.8]
3.6.8 32-bit reversed [17.2, 17.2, 17.2, 17.3, 17.3, 17.3, 17.3, 17.2, 17.2, 17.2]
3.6.8 64-bit iter [15.0, 14.9, 15.9, 14.9, 14.9, 15.0, 14.9, 14.9, 14.9, 14.9]
3.6.8 64-bit reversed [15.8, 15.9, 16.4, 15.9, 15.9, 16.0, 15.8, 15.9, 15.9, 15.8]
3.7.6 32-bit iter [16.6, 17.2, 16.6, 16.5, 16.7, 16.7, 16.5, 16.5, 16.5, 16.7]
3.7.6 32-bit reversed [17.2, 17.6, 17.0, 17.0, 16.9, 17.2, 17.3, 17.0, 17.5, 17.0]
3.7.6 64-bit iter [14.8, 15.1, 14.9, 14.9, 14.8, 15.1, 14.9, 14.8, 15.0, 14.9]
3.7.6 64-bit reversed [16.0, 20.1, 15.7, 15.6, 15.6, 15.6, 15.7, 15.7, 15.8, 15.5]
3.8.1 32-bit iter [16.4, 16.6, 16.3, 16.4, 16.5, 16.4, 16.5, 16.4, 16.8, 16.4]
3.8.1 32-bit reversed [22.3, 22.4, 22.2, 22.3, 22.3, 22.3, 22.5, 22.4, 22.3, 22.1]
3.8.1 64-bit iter [14.6, 15.1, 14.6, 14.7, 14.7, 14.7, 14.7, 14.6, 14.6, 14.6]
3.8.1 64-bit reversed [15.5, 16.1, 15.5, 15.6, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5]
对具有一百万个值的列表进行相同的测试 (list(range(250)) * 4000)。每个循环的时间是毫秒:
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 19.8 19.9 22.4 22.7
3.6.8 19.8 19.9 22.3 22.6
3.7.6 19.9 19.9 22.3 22.5
3.8.1 19.8 24.9 22.4 22.6
变化甚至更小,除了 3.8.1 32 位上的 reversed 再次慢得多。
还有一个,只是使用 CPython 3.8.0 而不是 3.8.1,它也会发生。
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 19.5 19.6 21.9 22.2
3.6.8 19.5 19.7 21.8 22.1
3.7.6 19.5 19.6 21.7 22.0
3.8.0 19.4 24.5 21.7 22.1
【问题讨论】:
-
我无法重现这个。在这两种情况下,我每个循环的触摸时间都低于 3.5 微秒。通过 Windows 10 上的 WSL 在 Ubuntu 上运行 Python 3.8.1。通过 Anaconda 在 Windows 10 上运行 Python 3.7.4 在这两种情况下每个循环的时间都低于 4 µsec。
-
我在第一个示例中得到了非常相似的数字:3.55/3.63 ... 使用 debian tho。
-
同样,我使用 Windows 10 的所有数字都相似。
-
@HeapOverflow,我不确定。我知道这很令人沮丧;这对我来说也很令人沮丧。我很想告诉你“运行命令
x并显示输出”......你能在其他机器上重现吗?与其他版本的 Python?你试过干净的 virtualenv 吗? -
"有点不想为此安装其他人" 如果您是唯一可以复制它但您不想做这项工作的人,请不要不要指望别人为你做这件事。
标签: python performance 64-bit 32-bit