(更新:可能仅限于Windows 32位的CPython 3.8,所以如果您在其他版本中无法复制,请不要感到惊讶。请参见更新部分中的表格。)
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
为什么反向迭代器速度要慢得多?
我正在使用CPython 3.8.1 32位版本,在Windows 10 Pro 64位版本1903上,配有Intel i5-7200U(这是一款华为MateBook X笔记本电脑)。没有特别的配置,只是在普通的Windows安装上进行了普通的Python安装。
更新:我在另一台机器上(Pentium N3700,Windows 10 Pro 64位1903),使用八个不同的Python版本(均使用默认设置)运行了一个更大的自动化测试。每次循环的时间以微秒为单位。
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项测试,每一轮测试都是10次的,上面显示的结果是其十个源时间中最好的。每个源时间的测试方法如下:
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))"
每个测试的时间都很稳定。完整表格(请注意,轮换是指我按列运行这些测试,而不是按行):
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
变化非常小,除了在32位的3.8.1上使用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
x
并向我展示输出”……你能在其他机器上重现吗?使用其他版本的Python?你尝试过在干净的虚拟环境中吗? - Chris