当前位置: 首页 > 知识库问答 >
问题:

迭代列表的奇怪速度差异

姚麒
2023-03-14

我创建了两个长列表,重复两个不同的值。在第一个列表中,值交替出现,在第二个列表中,一个值出现在另一个值之前:

a1 = [object(), object()] * 10**6
a2 = a1[::2] + a1[1::2]

然后我迭代它们,对它们不做任何操作:

for _ in a1: pass
for _ in a2: pass

两者哪个迭代更快?取决于我如何测量!我用每种计时方法跑了50场比赛:

timing method: timeit.timeit
  list a1 won 50 times
  list a2 won 0 times

timing method: timeit.default_timer
  list a1 won 0 times
  list a2 won 50 times

为什么这两种计时方法给我的结果完全相反?为什么这两个列表之间存在速度差异?我希望有两次25-25,而不是50-0和0-50。

之前类似问题的原因以及我认为他们不应该对此负责的原因:

  • 分支预测:我没有任何分支可以有所作为。
  • 缓存未命中:不应该发生,因为我只有两个值(我甚至没有用它们做任何事情)。
  • 垃圾回收机制:这里不涉及。
  • 无论如何,这些都无法解释两种计时方法之间相反的速度差异。

我使用的是Python3.10,在一台较弱的Windows笔记本电脑和一个强大的LinuxGoogle计算引擎实例上得到了相同的结果。

完整代码:

from timeit import timeit, default_timer

a1 = [object(), object()] * 10**6
a2 = a1[::2] + a1[1::2]

for method in 'timeit', 'default_timer':
    wins1 = wins2 = 0

    for _ in range(50):

        time1 = time2 = float('inf')
        for _ in range(5):

            if method == 'timeit':
                t1 = timeit('for _ in a1: pass', 'from __main__ import a1', number=1)
                t2 = timeit('for _ in a2: pass', 'from __main__ import a2', number=1)
            else:
                start = default_timer();
                for _ in a1: pass
                end = default_timer()
                t1 = end - start

                start = default_timer();
                for _ in a2: pass
                end = default_timer()
                t2 = end - start

            time1 = min(time1, t1)
            time2 = min(time2, t2)

        wins1 += time1 < time2
        wins2 += time2 < time1

    print(f'timing method: timeit.{method}')
    print(f'  list a1 won {wins1} times')
    print(f'  list a2 won {wins2} times')
    print()

共有1个答案

桂杰
2023-03-14

不是“答案”,而是一条线索:添加

def f(a):
    for _ in a: pass

然后在default\u timer分支中替换

for _ in a1: pass

对于f(a1),对于a2上的迭代也是如此。

我看到两件事:

  1. 输出更改为更统一
timing method: timeit.timeit

  list a1 won 50 times
  list a2 won 0 times

timing method: timeit.default_timer
  list a1 won 50 times
  list a2 won 0 times

#2是如此明显,可能很难注意到原因;-)在原始版本中,for循环目标\uu是一个全局模块,因此在每次迭代中更新它需要dict查找和存储。但是timeit用传递给它的内容构建一个函数,因此在运行的代码timeit中,\uu是一个局部变量,更快的STORE\u FAST索引存储就足够了。

引入了函数f(),以便在这两种情况下对局部变量进行“繁重的提升”。

在这里被计时的代码做的非常少,以致于一个判断查找和一个带有一个int的索引向量操作之间的差异可能是非常显著的

 类似资料:
  • 我在写需要加密和解密文件的应用程序。我的问题是解密比加密慢5倍。我已经删除了所有的文件读/写操作,并且只对加密进程进行了基准测试。结果非常令人惊讶: 使用(是javax.crypto.cipher的实例)加密1.5MB字节数组 我很惊讶,因为我知道AES解密和加密是对称的过程,在加密和解密速度上应该没有区别。 我使用密码,密钥为256位。

  • 问题内容: 我想知道是否有一种更快,更省时的方法来遍历元组列表,找到合适的匹配项。我要做的是: 根据列表中项目的数量,此代码可能需要花费一些时间才能执行。我敢肯定有更好的方法可以做到这一点。 问题答案: 假设更多的内存使用不是问题,并且如果元组的第一项是可哈希的,则可以从元组列表中创建字典,然后查找值就像从中查找键一样简单。就像是: 编辑 :要创建一个反向映射,请使用类似:

  • 问题内容: 我不了解python中列表的行为: 为什么一个元素的分配影响另一个元素?感谢您的回答! 问题答案: 当您将一个列表相乘时,它会复制对该列表的引用,但不会创建该列表的副本。由于列表是可变的,因此在更改列表时,所有对其引用的引用都会更改。 用ASCII术语来说: 您可以清楚地看到更改list3将同时影响两个位置。 如果要创建可变长列表而不复制引用,则应执行以下操作: 在这里,我们使用列表理

  • 问题内容: 我有一些面板内的组合框: 在我将项目更新到.NET 4之前还可以,在将项目(和AJAX)更新到.net4之后,这看起来真的很奇怪……我无法解释正确,我将显示: 我该如何解决?:)完整的CSS / ASPX页面在这里-> https://github.com/nCdy/Issues/tree/master/Ajax%20ComboBox(字符串#287) 问题答案: 我有一个类似的问题,

  • 问题内容: 在Python(2.7)中发现了从未有过的有趣的东西。 这个: 确实有效,结果是: 但 给 有人可以解释为什么吗?感谢您的回答。 问题答案: Python区分和运算符,并为它们提供了单独的挂钩。和。该类型只是为后者提供了不同的实现。 列表分别实现这些功能更为有效;必须返回一个全新的列表,而可以扩展然后返回。 在C代码中,是由所实现的,该只需调用,或者在python代码中,由。后者根据设

  • 主要内容:如何为每一行添加序号?本文章将介绍Thymeleaf标准表达式语法中的概念。我们将使用标记在模板中迭代产品列表。 编辑源代码以便将产品列表显示为表格行。已经将类的对象列表设置为具有变量名称的上下文模型(参考:中的实现)。 如果要上机实践,请参考:Thymeleaf+SpringMVC5示例项目。这里不再重复创建项目的过程,这里将只介绍如何使用标准表达式和标签。 这里创建一个Maven Web项目: thymeleaf-