【问题标题】:Numpy is very slow for basic array operationsNumpy 对于基本的数组操作非常慢
【发布时间】:2019-04-08 12:09:25
【问题描述】:

我有一个代码,我用一堆数字数据(即多个数组)进行了很多基本的算术计算。我已经意识到,在大多数可以想象的操作中,numpy 类总是比默认的 python 类慢。这是为什么呢?

例如,我有一个简单的 sn-p,我所做的只是用从另一个 numpy 数组中检索到的另一个元素更新 1 个 numpy 数组元素,或者我用另外 2 个 numpy 数组元素的数学乘积更新它。它应该是一个基本操作,但它总是比使用list 慢至少 2-3 倍。

首先我认为这是因为我没有协调数据结构,编译器必须做很多不必要的转换。因此,我重新编码了整个内容,并将每个float 替换为numpy.float64,并将每个list 替换为numpy.ndarray,整个代码中的整个数据都是numpy.float64,因此它不必做任何不必要的事情转换。

代码仍然比我只使用 listfloat 慢 2-3 倍。

例如:

    ALPHA       = [[random.uniform(*a_param)    for k in range(l2)] for l in range(l1)]
    COEFF       = [[random.uniform(*c_param)    for k in range(l2)] for l in range(l1)]

    summa=0.0
    for l in range(l1):
        for k in range(l2):
            summa+=COEFF[l][k] * ALPHA[l][k]

总是比以下速度快 2-3 倍:

    ALPHA       = numpy.random.uniform(*a_param, (l1,l2))
    COEFF       = numpy.random.uniform(*c_param, (l1,l2))

    summa=0.0
    for l in range(l1):
        for k in range(l2):
            summa+=COEFF[l][k] * ALPHA[l][k]

这怎么可能,我做错了什么,因为 numpy 应该加快速度。

作为记录,我正在使用Python 3.5.3numpy (1.12.1),我应该更新吗?

【问题讨论】:

  • 没有看到代码来复制您的问题,很难帮助您。向我们展示您认为比给定的纯 Python 等效代码慢的确切代码。
  • 请出示您的代码,否则您是在说 “我有这段代码你看不到,你能告诉我为什么它比你看不到的其他代码慢吗? ".
  • 好的,我添加了一些用于说明的代码,例如这样的东西,还有我只是用第 4 个数组的 1 个元素更新第 3 个数组的 1 个元素等等的东西。使用 Numpy,每个基本的数组操作都会变慢。
  • 使用循环操作 NumPy 数组总是很慢,甚至比使用 Python 列表还要慢。但是,使用 NumPy 执行 summa = (ALPHA * COEFF).sum() 应该会更快。在需要对 NumPy 数组使用基于循环的逻辑的情况下,可以考虑使用 Numba 进行快速 JIT 编译代码。
  • COEFF[l][k] 不是访问数组元素的惯用方式。 [l,k] 更好。它对速度没有太大影响,但它表明您只是对 numpy 文档进行了肤浅的阅读。

标签: arrays python-3.x list numpy


【解决方案1】:

修改 NumPy 数组的单个元素预计不会比修改 Python 列表的单个元素更快。当您对整个数组(或数组的子集)执行“矢量化”操作时,使用 NumPy 会得到加速。尝试将 NumPy 数组的前 10000 个元素分配为等于另一个数组的前 10000 个元素,并将其与使用列表进行比较。

如果您的数据和/或操作非常小(一个或几个元素),您最好不要使用 NumPy。

【讨论】:

  • 不仅如此,即使使用像float64 这样的numpy 数据结构也已经比使用简单的float 慢。我认为 numpy 在内存中存储数据的方式比从简单的 listfloats 中检索数据的速度要慢。
【解决方案2】:

我尝试了两件事:

  1. 运行您的两个代码块。对我来说,它们的速度差不多。
  2. 编写一个利用 numpy 向量化数学的新函数。这比其他方法快几倍。

这是我的功能:

import numpy as np

def with_lists(l1, l2):
    ALPHA = [[random.uniform(0, 1) for k in range(l2)] for l in range(l1)]
    COEFF = [[random.uniform(0, 1) for k in range(l2)] for l in range(l1)]
    summa=0.0
    for l in range(l1):
        for k in range(l2):
            summa+=COEFF[l][k] * ALPHA[l][k]
    return summa

def with_arrays(l1, l2):
    ALPHA = np.random.uniform(size=(l1,l2))
    COEFF = np.random.uniform(size=(l1,l2))
    summa=0.0
    for l in range(l1):
        for k in range(l2):
            summa+=COEFF[l][k] * ALPHA[l][k]
    return summa

def with_ufunc(l1, l2):
    """Avoid the loop completely by exploitng numpy's
    elementwise math."""
    ALPHA = np.random.uniform(size=(l1,l2))
    COEFF = np.random.uniform(size=(l1,l2))
    return np.sum(COEFF * ALPHA)

当我比较速度时(我在 IPython 中使用 %timeit 魔法),我得到以下信息:

>>> %timeit with_lists(10, 10)
107 µs ± 4.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit with_arrays(10, 10)
91.9 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit with_ufunc(10, 10)
12.6 µs ± 589 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

第三个函数,没有循环,在我的机器上大约快 10 到 30 倍,具体取决于 l1l2 的值。

【讨论】:

    猜你喜欢
    • 2021-09-29
    • 2013-01-19
    • 2011-08-11
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2018-06-20
    • 2015-08-22
    相关资源
    最近更新 更多