问题 Python 3.4中的切片速度是否真的慢?


这个  和我的 回答 让我思考Python 2.7和Python 3.4之间的这种特殊区别。以简单的示例代码为例:

import timeit
import dis

c = 1000000
r = range(c)
def slow():
    for pos in range(c):
        r[pos:pos+3]

dis.dis(slow)

time = timeit.Timer(lambda: slow()).timeit(number=1)
print('%3.3f' % time)

在Python 2.7中,我始终如一 0.165~ 而对于Python 3.4我一直都有 0.554~。反汇编之间唯一重要的区别是Python 2.7发布了 SLICE+3 Python 3.4发出时的字节代码 BUILD_SLICE 其次是 BINARY_SUBSCR。请注意,我已经从其他问题中消除了潜在减速的候选者,即字符串和事实 xrange 在Python 3.4中不存在(它应该与后者类似) range 无论如何班级)。

运用 itertools'  islice 在两者之间产生几乎相同的时间,所以我高度怀疑这是切片,这是造成差异的原因。

为什么会发生这种情况,是否有链接到记录行为变化的权威来源?

编辑:为了回答答案,我已经包装了 range 对象 list,确实给出了明显的加速。但是随着我增加了迭代次数 timeit 我注意到时间差异变得越来越大。作为一个完整性检查,我用切换替换了切片 None 看看会发生什么。

500次迭代 timeit

c = 1000000
r = list(range(c))
def slow():
    for pos in r:
        None

产量 10.688 和 9.915 分别。用。替换for循环 for pos in islice(r, 0, c, 3) 产量 7.626 和 6.270 分别。更换 None 同 r[pos] 产生 20~ 和 28~ 分别。 r[pos:pos+3] 产量 67.531 和 106.784 分别。

如您所见,时间差异很大。同样,我仍然相信这个问题与此没有直接关系 range


12394
2018-05-05 17:26


起源

你在用吗? range 要么 xrange 在Python 2? - cdarke
再试一次,用 r = list(range(c))。 - Robᵩ
Python 3 range 和Python 2 xrange 在这种情况下,对象不相似。 Python 3 range 对象支持切片,Python 2 xrange 对象不支持切片。 - cdarke
@cdarke在这种情况下它是无关紧要的,因为既没有链接的问题,也没有使用xrange。我只提到有人提出来。 - uh oh somebody needs a pupper


答案:


在Python 2.7上,您将遍历列表并切片列表。在Python 3.4上,你正在迭代a range 和 切片 range

当我在两个Python版本上运行带有列表的测试时:

from __future__ import print_function
import timeit
print(timeit.timeit('x[5:8]', setup='x = list(range(10))'))

我明白了 Python 2.7上的0.243554830551秒 和 Python 3.4上的0.29082867689430714秒,差异小得多。


消除后你看到的性能差异 range 对象要小得多。它主要来自两个因素:Python 3上的添加速度稍慢,而Python 3需要通过 __getitem__ 用切片对象进行切片,而Python 2则有 __getslice__

我无法复制你看到的时间差异 r[pos];你可能在这个测试中遇到了一些混淆因素。


10
2018-05-05 17:34



增加迭代次数开始显示时间差异越来越大。 1.352 和 2.171 分别进行10次迭代。我不确定我在这里缺少什么。 - uh oh somebody needs a pupper
@ user6292850:你是如何运行该测试的? - user2357112
我只是在增加数字参数 timeit,并运行 python test.py 其次是 python3 test.py。 - uh oh somebody needs a pupper
@ user6292850:我运行了一些测试并将主要贡献者与剩余的性能差异隔离开来。我的测试用 r[pos] 但是,与40%的运行时差异不同,它只显示了10%的运行时差异。 - user2357112
@ user6292850里面 test.py 你在测试吗?您的问题还是答案中的问题?让我们测试同样的事情。我建议使用这种格式: python -m timeit -s "r = list(range(1000))" "for pos in range(1000): r[pos:pos+3]"。为什么不使用1000000的默认迭代次数? - warvariuc