Numba:手动循环比使用numpy数组的a += c * b更快吗?

6

我想使用numbanumpy中执行'daxpy'操作(向矢量添加第二个矢量的标量倍数并将结果分配给第一个),通过以下测试,我发现手写循环比执行a += c * b更快。

我没有预料到这样的结果。这种行为的原因是什么?

import numpy as np
from numba import jit

x = np.random.random(int(1e6))
o = np.random.random(int(1e6))
c = 3.4

@jit(nopython=True)
def test1(a, b, c):
    a += c * b
    return a

@jit(nopython=True)
def test2(a, b, c):
    for i in range(len(a)):
        a[i] += c * b[i]
    return a

%timeit -n100 -r10 test1(x, o, c)
>>> 100 loops, best of 10: 2.48 ms per loop
%timeit -n100 -r10 test2(x, o, c)
>>> 100 loops, best of 10: 1.2 ms per loop
1个回答

4
需要翻译的内容如下:

请记住,在numba中进行“手动循环”非常快,基本上与numpy操作使用的c循环相同。

在第一个示例中,有两个操作,分配/计算了一个临时数组(c * b),然后将该临时数组添加到a中。在第二个示例中,两个计算在同一个循环中进行,没有中间结果。

理论上,numba可以融合循环并优化#1以执行与#2相同的操作,但它似乎没有这样做。如果您只想优化numpy操作,则numexpr也可能值得一看,因为它专门为此而设计 - 虽然可能不会比显式融合循环更好。

In [17]: import numexpr as ne

In [18]: %timeit -r10 test2(x, o, c)
1000 loops, best of 10: 1.36 ms per loop

In [19]: %timeit ne.evaluate('x + o * c', out=x)
1000 loops, best of 3: 1.43 ms per loop

谢谢你的回答。我本来以为通过使用 += 运算符,numba 就能够很明显地高效地解决它。我认为这里是一种特殊情况,因为例如点积 np.dot 在速度方面是无法被超越的。 - NoBackingDown
这有些离题了,但实际上np.dot是一个特殊情况。numpy会调用你安装的各种BLAS库,可能是一些高度优化的库,比如英特尔的MKL。 - chrisb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接