【问题标题】:NumPy: Alternative to `vectorize` that lets me access the arrayNumPy:可让我访问数组的“vectorize”的替代方案
【发布时间】:2020-07-10 06:29:20
【问题描述】:

我有这个代码:

output_array = np.vectorize(f, otypes='d')(input_array)

我想用这段代码替换它,它应该给出相同的输出:

output_array = np.ndarray(input_array.shape, dtype='d')
for i, item in enumerate(input_array):
    output_array[i] = f(item)

我想要第二个版本的原因是我可以在计算时在单独的线程中开始迭代 output_array。 (是的,我知道 GIL,这部分已经处理好了。)

不幸的是,for 循环非常慢,即使我没有在单独的线程上处理数据。我在我的目标平台 CPython 和 PyPy3 上对它进行了基准测试。在 CPython 上它比 vectorize 慢 3 倍,在 PyPy3 上它比 vectorize 慢 67 倍!

尽管 Numpy 文档说“提供vectorize 函数主要是为了方便,而不是为了性能。实现本质上是一个 for 循环。”

知道为什么我的实现很慢,以及如何快速实现仍然允许我在完成之前使用output_array

【问题讨论】:

  • 首先,不要使用np.ndarray。用np.zerosnp.empty 初始化一个数组。但话虽如此,我很惊讶np.vectorize 更快。告诉我们一些关于函数的事情。还有input_array - 什么是dtypeshape(大致)。
  • 我不确定它的相关性如何,但它基本上做了一堆 math.sin 动作、乘法和指数。这里是get_pressure 方法:github.com/cool-RR/python_synthesizer/blob/master/…
  • 您能否将math.sin(等)替换为np.sin 并跳过vectorize 和循环?
  • 我考虑过这一点,但我必须改变我的程序的整个构建方式,如果可能的话,我想避免这种情况。

标签: python numpy pypy


【解决方案1】:

塞巴斯蒂安·伯格给了我一个解决方案。当迭代输入数组中的项目时,使用item.item() 而不仅仅是item。这会将numpy.float64 对象转换为普通的 Python 浮点数,让一切变得更快并解决我的特殊问题 :)

【讨论】:

    【解决方案2】:

    numpy 中的向量化通过 for 循环提供数值计算的性能提升,因为它减少了一些 Python 解释器在动态确定数据的某些特征时的开销(比如它的类型和位置)在运行时。

    ndarray 对象执行了许多优化计算速度的强大功能,例如确保其包含的数据在内存中是同质且连续的。

    对于 n 维数据,默认情况下,numpy 将值存储在C ordering (row major) 中,这样一行的连续元素彼此相邻存储。

    它也是一个数据块的跨步视图,这意味着当你初始化它时,你也准确地说明了其中每个项目占用了多少字节的内存——换句话说,您需要迈出多大的步伐才能进入下一个阶段。

    当您在 ndarray 对象上使用矢量化函数时,它的行为更像 C 代码而不是 Python。也就是说,它直接对内存中的值进行操作并修改它们,并利用所有存储和类型优化。

    我怀疑,当您没有对函数 f 进行矢量化处理时,会增加 Python 解释器的开销。当它成功矢量化时,该函数的大部分将在 C 中执行,并避免 Python 造成的大部分减速。我想知道 enumerate 是否未能像 numpy 的 nditer 对象那样利用底层数据结构,但我用 enumerate 和 @987654331 对 nditer、numpy ufunc 和显式 for 循环进行了基准测试@ 实际上是最快的迭代器,所以我猜你的自定义函数可能是时间的罪魁祸首。这是有道理的,特别是考虑到 PyPy 的减速非常更加剧烈。

    示例基准:

    a = np.ndarray()
    
    >>> %%time
    >>> for x in np.nditer(a, flags=['external_loop']):
    >>> ....    x*x
    CPU times: user 201 ms, sys: 219 ms, total: 420 ms
    Wall time: 420 ms
    
    >>> %%time
    >>> np.square(a)
    CPU times: user 201 ms, sys: 180 ms, total: 381 ms
    Wall time: 380 ms
    
    >>> %%time
    >>> for i, x in enumerate(a):
    >>> ....    x*x
    
    CPU times: user 78.5 ms, sys: 1.79 ms, total: 80.3 ms
    Wall time: 79.4 ms
    

    【讨论】:

    • “我猜你的自定义函数可能是时间的罪魁祸首”当快速版本也调用我的自定义函数时,这怎么可能呢?
    • 当你成功向量化一个函数时,numpy 会将 Python 函数中实际发生的很多事情委托给 C 代码,而不是依赖 Python 解释器来完成所有繁重的工作,这就是首先要对 Python 函数进行矢量化——以加快它们的速度。
    • 我通过调试器确认 vectorize 实际上正在调用我的 Python 函数,将控制权交还给 Python 解释器,并使用诚实的方式逐一运行所有行- 上帝浮动和整数。
    • 也许它与您访问内存的方式有关,然后 -- 由于跨步和利用行主要格式,矢量化将非常有效,但我不认为带有枚举和括号索引的 for 循环可以获得相同的效率。
    • 我明白了。 NumPy 是否提供了类似于 vectorize 的任何东西,让我可以在数组完成计算所有结果之前访问它?
    猜你喜欢
    • 2018-07-29
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多