【问题标题】:Numpy vectorisation of python object arraypython对象数组的numpy向量化
【发布时间】:2015-04-01 08:52:53
【问题描述】:

只是一个简短的问题,在我出发之前我找不到答案,

当我做这样的事情时:

v1 = float_list_python = ... # <some list of floats>
v2 = float_array_NumPy = ... # <some numpy.ndarray of floats>
                             # I guess they don't have to be floats - 
                             # but some object that also has a native 
                             # object in C, so that numpy can just use
                             # that

如果我想将这些向量乘以标量,我的理解一直是 python 列表是对象引用的列表,因此循环遍历列表进行乘法必须获取所有浮点数的位置,并且然后必须得到花车才能做到这一点 - 这是它很慢的原因之一。

如果我在 NumPy 中做同样的事情,那么,我不确定会发生什么。我想可能会发生很多事情:

  1. 它将乘法拆分到各个核心。
  2. 它矢量化了多阳离子(也是?)

我发现的文档表明,numpy 中的许多原语都尽可能利用第一个选项(目前我手头没有计算机,我可以对其进行测试)。我的直觉告诉我,只要有可能,第二个就应该发生。

所以我的问题是,如果我创建一个 Python 对象的 NumPy 数组,它至少还会并行执行列表上的操作吗?我知道,如果你创建一个对象数组,本机 C 类型,那么它实际上会在实际对象的内存中创建一个连续的数组,如果你创建一个 python 对象的 numpy 数组,它将创建一个引用数组,但我不明白为什么这会排除并行所述列表上的操作,并且找不到任何明确声明的地方。

编辑:我觉得我在问什么有点困惑。我了解向量化是什么,我了解它是一种编译器优化,而不是您必须在其中编程的东西(尽管对齐数据使其在内存中连续很重要)。基于矢量化,我只想知道 numpy 是否使用它。如果我执行np_array1 * np_array2 之类的操作,底层库调用是否使用矢量化(假设dtype 是兼容类型)。

对于内核的拆分,我的意思是,如果我再次执行 np_array1 * np_array2 之类的操作,但这次是 dtype=object:它会在内核之间划分工作吗?

【问题讨论】:

    标签: python numpy scipy


    【解决方案1】:

    numpy 速度很快,因为它在快速编译的C 代码中执行类似这样的数字运算。相比之下,列表操作在解释的 Python 级别上运行(尽可能使用 Python 字节码等进行简化)。

    数字类型的numpy 数组将这些数字存储在数据缓冲区中。至少在简单的情况下,这只是C 代码可以有效地单步执行的一个字节块。该数组还具有允许多维访问的形状和步幅信息。

    当您将数组乘以一个标量时,它实际上调用了一个名为“multiply_array_by_scalar”之类的 C 函数,该函数在快速编译的代码中进行乘法运算。因此这种numpy 操作速度很快(与 Python 列表代码相比),无论内核数量或其他多处理/线程增强功能如何。

    对象数组没有任何特殊的速度优势(与列表相比),至少目前没有。

    看看我对创建数组数组的问题的回答,https://stackoverflow.com/a/28284526/901925 我不得不使用迭代来初始化这些值。

    你做过时间实验吗?例如,构造一个数组,比如(1000,2)。使用tolist() 创建等效的列表列表。并制作一个类似的对象数组,每个对象都是一个 (2,) 数组或列表(这需要多少工作?)。现在为每个子列表做一些简单的事情,比如len(x)

    【讨论】:

    • 我知道 numpy 速度很快,因为它使用已编译的 C 代码,这不是我在徘徊的。我知道当它使用 c 对象时,它们在内存中是连续的,因此由于缓存未命中次数减少,并且预取器能够完成它的工作,它也获得了非常大的性能奖励。
    • 另外,您对关于在阵列周围设置不可穿透的墙的问题的回答我认为只是来自不了解 numpy 如何处理您发送的切片对象。 ndarray__getitem__ 函数被设计成最多只能接受 N 个 slice 对象,其中 N 是 ndarray 的维度,在您的情况下是 2。就是这样.看看here.
    • 参考其他线程,b 是一个二维数组。我必须选择b 的单个元素才能对该元素的内容进行任何索引(或数学运算)。没有为b 的多个元素提供矢量化索引或数学运算。这就是我说的墙的意思。可以定义跨越该边界的操作。 np.char 有许多可用于字符串 dtype 的操作。
    【解决方案2】:

    @hpaulj 为您的问题提供了一个很好的答案。一般来说,通过阅读您的问题,我发现您实际上并不了解“矢量化”在幕后的作用。这篇文章很好地解释了矢量化以及它如何实现更快的计算 - http://quantess.net/2013/09/30/vectorization-magic-for-your-computations/

    关于第 1 点 - 跨多个内核分布计算,Numpy 并非总是如此。然而,像numexpr 这样的库支持多线程、高效的 Numpy 数组计算,并支持几个基本的逻辑和算术运算符。与 Numpy 结合使用时,Numexpr 可用于加速关键计算,因为它避免了在内存中为矢量化例程复制大型数组(如 Numpy 的情况),并且可以使用系统上的所有内核来执行计算。

    【讨论】:

    • 我了解向量化是什么,并且它显然不能用于对象数组(我认为问题中暗示了“它是否仍然至少会并行执行列表上的操作”部分)。至于回答问题(你的第二段),“情况并非总是如此” - 你知道具体是什么时候吗?
    • 这取决于是否编译numpy/scipy以便链接到实现并行化的库。例如,如果您使用已编译的 Numpy 与 MKL 库一起运行,英特尔有 MKL 库可以加速 numpy 计算。这是 Anaconda 的付费选项(如果您是学生,则免费)。所以没有一个通用的、通用的答案。单个 numpy 操作是否支持多线程/并行化与您的系统架构、可用的库以及本地 Numpy 安装编译使用的库有关。
    • 更多信息可能在这里有用:更多信息:wiki.scipy.org/ParallelProgramming
    • 好吧,这更有意义。谢谢。它在从源代码部分构建的 numpy 安装页面上也提到了它,所以看起来任何人都可以根据需要免费获得它。
    猜你喜欢
    • 2011-09-02
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-23
    相关资源
    最近更新 更多