为什么本机 python 列表上的 for 循环比 numpy 数组上的 for 循环更快

2024-03-19

我正在阅读介绍 numpy 的章节高性能Python并在我自己的计算机上使用了代码。我无意中用 for 循环运行了 numpy 版本,发现与本机 python 循环相比,结果出奇地慢。

代码的简化版本如下,其中我定义了一个值为 0 的二维数组 X 和另一个值为 1 的二维数组 Y,然后重复将 Y 添加到 X,概念上是 X += Y。

import time
import numpy as np

grid_shape = (1024, 1024)

def simple_loop_comparison():
    xmax, ymax = grid_shape

    py_grid = [[0]*ymax for x in range(xmax)]
    py_ones = [[1]*ymax for x in range(xmax)]

    np_grid = np.zeros(grid_shape)
    np_ones = np.ones(grid_shape)

    def add_with_loop(grid, add_grid, xmax, ymax):
        for x in range(xmax):
            for y in range(ymax):
                grid[x][y] += add_grid[x][y]

    repeat = 20
    start = time.time()
    for i in range(repeat):
        # native python: loop over 2D array
        add_with_loop(py_grid, py_ones, xmax, ymax)
    print('for loop with native list=', time.time()-start)

    start = time.time()
    for i in range(repeat):
        # numpy: loop over 2D array
        add_with_loop(np_grid, np_ones, xmax, ymax)
    print('for loop with numpy array=', time.time()-start)

    start = time.time()
    for i in range(repeat):
        # vectorized numpy operation
        np_grid += np_ones
    print('numpy vectorization=', time.time()-start)

if __name__ == "__main__":
    simple_loop_comparison()

结果如下:

# when repeat=10
for loop with native list= 2.545672655105591
for loop with numpy array= 11.622980833053589
numpy vectorization= 0.020279645919799805

# when repeat=20
for loop with native list= 5.195128440856934
for loop with numpy array= 23.241904258728027
numpy vectorization= 0.04613637924194336

我完全期望 numpy 向量化操作优于其他两个,但我惊讶地发现在 numpy 数组上使用 for 循环结果明显慢于本机 python 列表。我的理解是,至少缓存应该用 numpy 数组相对填充得很好,即使使用 for 循环,它也应该优于没有矢量化的列表。

是否有关于 numpy 或 CPU/缓存/内存如何在低级别工作的我不明白的事情?非常感谢。

编辑:更改标题


一个更简单的情况 - 列表与数组的列表理解:

In [119]: x = list(range(1000000))
In [120]: timeit [i for i in x]
47.4 ms ± 634 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [121]: arr = np.array(x)
In [122]: timeit [i for i in arr]
131 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

列表有一个数据缓冲区,其中包含指向内存中其他位置的对象的指针。因此,迭代或索引列表只需要查找该指针并获取对象:

In [123]: type(x[1000])
Out[123]: int

数组将其元素以字节形式存储在数据缓冲区中。获取元素需要(快速)找到这些字节,然后将它们包装在 numpy 对象中(根据 dtype)。这样的对象类似于 0d 单元素数组(具有许多相同的属性)。

In [124]: type(arr[1000])
Out[124]: numpy.int32

此索引不仅获取数字,还重新创建数字。

我经常将对象数据类型数组描述为增强或降级列表。与列表一样,它包含指向内存中其他位置的对象的指针,但它不能按append。我们经常说它失去了数值数组的许多优点。但它的迭代速度介于其他两者之间:

In [125]: arrO = np.array(x, dtype=object)
In [127]: type(arrO[1000])
Out[127]: int
In [128]: timeit [i for i in arrO]
74.5 ms ± 1.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

无论如何,我在其他答案中发现,如果必须迭代,请坚持使用列表。如果您从列表开始,那么坚持使用列表通常会更快。正如您注意到的numpy vector速度很快,但创建数组需要时间,这可能会抵消任何节省的时间。

比较从此列表创建数组所需的时间与从头开始创建此类数组所需的时间(使用已编译的 numpy 代码):

In [129]: timeit np.array(x)
109 ms ± 1.97 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [130]: timeit np.arange(len(x))
1.77 ms ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么本机 python 列表上的 for 循环比 numpy 数组上的 for 循环更快 的相关文章

随机推荐

  • Android Studio 3.0 发现未知元素

    我更新Android Studio后 我的一个项目无法构建 错误信息是 Error 20 error unknown element
  • 如何在 BigQuery 查询中转义百分号?

    我们尝试从 BigQuery 中仅包含百分号 的字段中选择行 我们已经尝试过各种其他问题的答案 如下所示 SELECT COUNT 1 FROM Table WHERE field name LIKE 这会产生带有单引号或双引号的无效转义字
  • preg_replace 行首的双空格到制表符 (\t)

    只是一个简单的快速问题 我想用制表符替换行开头的双空格 目前我正在尝试preg replace 2 t text 但这仅替换了第一次出现的双倍空格 EDIT preg replace PATTERN REPLACEMENT HalloWor
  • 在两种不同模型用户和活动管理员的情况下,如何定义设备的自定义故障?

    我有两种模式 用户 和 活动管理员 我想在这两种模式上应用我的devise集成 我有我的custom failure rb如下 class CustomFailure lt Devise FailureApp def redirect ur
  • 我应该关心大量的依赖关系吗?

    我正要包括HtmlUnit http htmlunit sourceforge net项目中的库 我解压了 zip 文件 发现它不少于12 个依赖项 http htmlunit sourceforge net dependencies ht
  • .vue 单文件组件中使用的基础问题

    我发现使用时有问题祖布基金会 http foundation zurb com班级在 vue 单文件组件 https v2 vuejs org v2 guide single file components html 起初我无法得到显示模态
  • VSTS 构建 Nuget 打包器不起作用

    我正在尝试在构建后打包一个库以在我的 VSTS Packages feed 上发布 但任务 Nuget Packager 不起作用 我尝试了很多设置组合 但无法使其发挥作用 我尝试使用 Net Core 和 Net 4 6 2 打包该库 尝
  • 在 ASP.NET Core MVC 中,是否可以从项目文件夹外部添加 View 文件夹?

    我的大部分观点都是有规律的
  • 如何检查 Python 引发异常的函数范围?

    我最近发现了 Python 中非常有用的 i 标志 i inspect interactively after running script also PYTHONINSPECT x and force prompts even if st
  • 相当于 De Bruijn LSB,但适用于 MSB

    有谁知道类似于 De Bruijn 的 LSB 但针对 MSB 的算法吗 或者确定 MSB 的最有效方法 我知道 Log 2 Val 会这样做 但我不知道这是否是最有效的方法 我需要它的原因是我需要将小端转换为大端 我知道这个的标准算法 然
  • 如何检查 PyTorch 是否正在使用 GPU?

    如何检查 PyTorch 是否正在使用 GPU 这nvidia smi命令可以检测 GPU 活动 但我想直接从 Python 脚本内部检查它 这些功能应该有助于 gt gt gt import torch gt gt gt torch cu
  • 在批处理和 VBS 混合中使用变量

    This thread https stackoverflow com questions 9074476 is it possible to embed and execute vbscript within a batch file w
  • Boost.Asio安装问题

    我已经使用 bjam install 安装了 boost 库 但是当我编译程序时 include boost asio hpp int main return 0 出现此类错误 tmp ccVR3eeF o In function stat
  • 虚拟化页表的工作原理

    阅读有关虚拟化页表概念的内容 其中部分页表放入虚拟内存中 维基百科 https en wikipedia org wiki Page table Virtualized page table以及 Patterson 和 Hennessy 页
  • ExecutorService,如何等待所有任务完成

    等待所有任务的最简单方法是什么ExecutorService完成 我的任务主要是计算 所以我只想运行大量作业 每个核心一个 现在我的设置如下所示 ExecutorService es Executors newFixedThreadPool
  • 如何将 NSDate 对象设置为午夜?

    我有一个NSDate对象 我想将其设置为任意时间 例如午夜 以便我可以使用timeIntervalSince1970一致检索数据的功能 无需担心时间when对象已创建 我尝试过使用NSCalendar并使用一些 Objective C 方法
  • 同构弦

    给定两个字符串 s 和 t 确定它们是否同构 如果 s 中的字符可以替换得到 t 则两个字符串是同构的 所有出现的字符都必须替换为另一个字符 同时保留字符的顺序 任何两个字符都不能映射到同一个字符 但一个字符可以映射到其自身 例如 给定 e
  • 如何在magento中调用另一个动作?

    是否可以在magento中调用另一个动作 例如 让我们考虑两种操作方法 添加动作 更新操作 调用 addAction 时是否可以实际调用 updateAction 谢谢 巴兰 您可以进行转发 例如 public function addAc
  • Objective-C 中的自动解析库 - [自动 XML/JSON 到对象转换]

    Objective C 中是否有一个我可以在 iPhone 中使用的库 在其中我可以提前告诉库这些标签应该在 xml 文件中出现 然后该库会自动为我解析它并给我一个 NSDictionary 数组作为回报或类似的东西 简而言之 我正在寻找一
  • 为什么本机 python 列表上的 for 循环比 numpy 数组上的 for 循环更快

    我正在阅读介绍 numpy 的章节高性能Python并在我自己的计算机上使用了代码 我无意中用 for 循环运行了 numpy 版本 发现与本机 python 循环相比 结果出奇地慢 代码的简化版本如下 其中我定义了一个值为 0 的二维数组