地图与星图的性能?

2024-06-03

我试图对两个序列进行纯Python(没有外部依赖)逐元素比较。我的第一个解决方案是:

list(map(operator.eq, seq1, seq2))

然后我发现starmap函数来自itertools,这看起来和我很相似。但事实证明,在最坏的情况下,在我的计算机上速度提高了 37%。因为这对我来说并不明显,所以我测量了从生成器检索 1 个元素所需的时间(不知道这种方式是否正确):

from operator import eq
from itertools import starmap

seq1 = [1,2,3]*10000
seq2 = [1,2,3]*10000
seq2[-1] = 5

gen1 = map(eq, seq1, seq2))
gen2 = starmap(eq, zip(seq1, seq2))

%timeit -n1000 -r10 next(gen1)
%timeit -n1000 -r10 next(gen2)

271 ns ± 1.26 ns per loop (mean ± std. dev. of 10 runs, 1000 loops each)
208 ns ± 1.72 ns per loop (mean ± std. dev. of 10 runs, 1000 loops each)

在检索元素方面,第二种解决方案的性能提高了 24%。之后,它们都产生相同的结果list。但从某个地方我们获得了额外 13% 的时间:

%timeit list(map(eq, seq1, seq2))
%timeit list(starmap(eq, zip(seq1, seq2)))

5.24 ms ± 29.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.34 ms ± 84.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我不知道如何更深入地分析此类嵌套代码?所以我的问题是为什么第一个生成器的检索速度如此之快,并且我们从中获得了额外的 13%list功能?

编辑: 我的第一个意图是执行逐元素比较而不是all, 所以all函数被替换为list。这种替换不会影响时序比。

Windows 10(64 位)上的 CPython 3.6.2


有几个因素(共同)导致观察到的性能差异:

  • zip重新使用返回的tuple如果它的引用计数为 1,则下一个__next__已拨打电话。
  • map建立一个new tuple每次都会传递给“映射函数”__next__已拨打电话。实际上,它可能不会从头开始创建新的元组,因为 Python 为未使用的元组维护了一个存储空间。但在那种情况下map必须找到一个未使用的大小合适的元组。
  • starmap检查可迭代中的下一项是否属于类型tuple如果是这样,它就会将其传递下去。
  • 从 C 代码中调用 C 函数PyObject_Call不会创建传递给被调用者的新元组。

So starmap with zip只会一遍又一遍地使用一个传递给的元组operator.eq从而极大地减少了函数调用的开销。map另一方面,每次都会创建一个新元组(或从 CPython 3.6 开始填充 C 数组)operator.eq叫做。所以实际上速度差异只是元组创建开销。

我将提供一些可用于验证这一点的 Cython 代码,而不是链接到源代码:

In [1]: %load_ext cython

In [2]: %%cython
   ...:
   ...: from cpython.ref cimport Py_DECREF
   ...:
   ...: cpdef func(zipper):
   ...:     a = next(zipper)
   ...:     print('a', a)
   ...:     Py_DECREF(a)
   ...:     b = next(zipper)
   ...:     print('a', a)

In [3]: func(zip([1, 2], [1, 2]))
a (1, 1)
a (2, 2)

Yes, tuples 并不是真正不可变的,一个简单的Py_DECREF就足以“欺骗”zip相信没有其他人拥有对返回元组的引用!

至于“元组传递”:

In [4]: %%cython
   ...:
   ...: def func_inner(*args):
   ...:     print(id(args))
   ...:
   ...: def func(*args):
   ...:     print(id(args))
   ...:     func_inner(*args)

In [5]: func(1, 2)
1404350461320
1404350461320

因此元组被直接传递(只是因为它们被定义为 C 函数!)这对于纯 Python 函数不会发生:

In [6]: def func_inner(*args):
   ...:     print(id(args))
   ...:
   ...: def func(*args):
   ...:     print(id(args))
   ...:     func_inner(*args)
   ...:

In [7]: func(1, 2)
1404350436488
1404352833800

请注意,如果被调用的函数不是 C 函数,即使是从 C 函数调用,也不会发生这种情况:

In [8]: %%cython
   ...: 
   ...: def func_inner_c(*args):
   ...:     print(id(args))
   ...: 
   ...: def func(inner, *args):
   ...:     print(id(args))
   ...:     inner(*args)
   ...:

In [9]: def func_inner_py(*args):
    ...:     print(id(args))
    ...:
    ...:

In [10]: func(func_inner_py, 1, 2)
1404350471944
1404353010184

In [11]: func(func_inner_c, 1, 2)
1404344354824
1404344354824

所以,有很多“巧合”导致了这一点:starmap with zip比打电话更快map当被调用函数也是 C 函数时具有多个参数...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

地图与星图的性能? 的相关文章

随机推荐

  • 如何在 pygame 中聚焦光线或如何仅绘制窗口的某些圆形部分?

    对于这一点 如果您熟悉它 请想想 超级马里奥制造2 中嘘关卡中的黑暗模式 我试图在角色周围创建一个圆形聚光灯 这也将使圆圈范围内的任何内容都可见 例如部分站在地板上 敌人或场景中的任何其他物体 我的计划是首先绘制圆圈 聚光灯 然后绘制场景
  • ASP.NET MVC区域命名空间问题

    我在我的 asp net mvc 3 解决方案中创建一个名为的新区域admin Visual Studio 自动分配名称空间 MyApp areas admin controllers 我将其更改为MyApp admin controlle
  • 如何在asp.net中异步执行两个作业

    网 我需要你的帮助请帮助我 请参阅我的代码 如果我的第一份工作完成 则退出btn ok代码隐藏并更新到 ASP NET 屏幕 但同时作业 2 必须工作 正在处理批量电子邮件 protected void btn ok object send
  • 从类外部调用某些异步方法时出现异常

    假设我有一个名为 x 和 y 的类 如下所示 class x public x p private async p await q private async p some logic is there 在 test aspx cs 文件中
  • 从对象获取数据 - 我看到数据但无法保存它们

    正如你所看到的 我是新来的 我确实尝试过搜索 但没有找到解决我问题的方法 所以这是我的问题 如果我这样做 console log grid data kendoGrid data 这在控制台中显示如下 所以我明白这一点 有一个数组和一个带有
  • 为什么 tf.matmul() 给出看似不一致的结果?

    sess tf InteractiveSession num elements 10 output 0 76158798 num elements softmax w 0 1 0 1 0 1 0 1 0 1 0 1 0 1 num elem
  • 使 Camel 路线并行运行

    我有一个应用程序使用骆驼路线执行一些基本的 ETL 每个路由都配置为从一个表中获取一些数据进行一些转换 并将其安全地保存到不同模式的同一个表中 所以骆驼路线和表之间存在一对一的关系 假设我有这两条路线 from direct table 1
  • 无法提取 Typescript 中的对象值

    我一直在尝试将 JavaScript Web 表单转换为 Typescript 但无法弄清楚如何处理以下内容 在 JavaScript 中有效 let fieldValues JSON parse cookieData let keys O
  • 为什么这个 oracle 批量插入不起作用?

    我正在尝试将一些数据批量插入到 oracle 数据库中 我按照文档中的示例进行操作 this DataBaseAccess new OracleConnection connString var dataAdapter new Oracle
  • Maven依赖关系无法解决

    我在用着阿帕奇Maven3两三天以来 一些依赖关系无法再解决 这首先没有问题 更具体地说 它是 maven findbgs plugin plugin 1 3 1 maven cobertura plugin plugin 1 3 它们不应
  • 将超链接添加到 PDF 文档中

    我目前正在扩展我们的自定义 PDF 编写器 以便能够编写网站链接 但是 我遇到了一个问题 因为我无法找到如何将链接放入 PDF 的任何地方 这是打印文本的内容 BT 70 50 TD F1 12 Tf visit my website Tj
  • SeekBar 未设置实际进度。 SetProgress() 不适用于早期的 Android 版本 [重复]

    这个问题在这里已经有答案了 我有一个应用程序 我想根据从 Web 服务检索到的值设置特定 SeekBar 的进度 但是 当我使用 SetProgress int 时 只有 SeekBar 的 搜索者 被设置 该栏未按预期 填充 它看起来是这
  • 我什么时候应该使用内联和外部 Javascript?

    我想知道什么时候应该包含外部脚本或将它们与 html 代码内联编写 就性能和易于维护而言 这方面的一般做法是什么 真实场景 我有几个需要客户端表单验证的 html 页面 为此 我使用了一个包含在所有这些页面上的 jQuery 插件 但问题是
  • C# 并行与并行线程代码性能

    我一直在测试 System Threading Parallel 与线程的性能 我很惊讶地发现并行比线程花费更长的时间来完成任务 我确信这是由于我对并行的了解有限 我刚刚开始阅读 我想我会分享一些片段 如果有人可以向我指出并行代码比线程代码
  • Maven:缺少工件 org.springframework:spring:jar:4.2.6

    我在 SpringToolSuite 中有一个动态 Web 项目 它被转换为 Maven 项目 我遇到问题 缺少工件 org springframework spring jar 4 2 6 我已经尝试清理 重建和运行该项目 它给 读取文件
  • jquery 事件委托

    我正在尝试使用事件委托重写一段代码 希望它能停止与另一个 js 代码段发生冲突 但我已经破解了密码 原来的 to scale up on hover var current h null var current w null piccon
  • 由于未经授权的错误,无法使用 dotnet CLI 将 nuGet 包推送到 GitLab

    GitLab 现在支持 nuget 公共和私有 feed 存储库 我有一个公共项目 例如 https gitlab com sunnyatticsoftware sasw test support https gitlab com sunn
  • 使一个 sbt 配置依赖于另一个配置

    sbt 文档显示了如何仅在项目之间声明依赖关系的示例 但我确信有方法可以声明一个配置依赖于另一个配置 就像测试配置使用编译配置中的类路径一样 如何声明我自己的配置 以便它依赖于编译配置生成的类路径 我更仔细地研究了建议的解决方案 然后又出现
  • for 循环和 into_iter() [重复]

    这个问题在这里已经有答案了 据我了解 for循环隐式调用into iter 在向量上循环遍历它 但是 在下面的代码中 为什么vec在 Question1 1 的第一个 for 循环中没有消耗 但在第二个 for 循环中 vec 消耗了 fn
  • 地图与星图的性能?

    我试图对两个序列进行纯Python 没有外部依赖 逐元素比较 我的第一个解决方案是 list map operator eq seq1 seq2 然后我发现starmap函数来自itertools 这看起来和我很相似 但事实证明 在最坏的情