python中的Numba jit警告解释

Cha*_*apo 7 python recursion numpy python-3.x numba

我已经定义了以下递归数组生成器,并且正在使用 Numba jit 来尝试加速处理(基于这个 SO 答案

@jit("float32[:](float32,float32,intp)", nopython=False, nogil=True)
def calc_func(a, b, n):
    res = np.empty(n, dtype="float32")
    res[0] = 0
    for i in range(1, n):
        res[i] = a * res[i - 1] + (1 - a) * (b ** (i - 1))
    return res
a = calc_func(0.988, 0.9988, 5000)
Run Code Online (Sandbox Code Playgroud)

我收到了一堆我不太明白的警告/错误。希望能帮助解释它们并使它们消失,以便(我假设)更快地加快计算速度。

他们在下面:

NumbaWarning:编译正在回退到启用循环提升的对象模式,因为函数“calc_func”类型推断失败,原因是:Function() 与类型参数的使用无效:(int64, dtype=Literalstr) * 参数化

在定义 0 中:所有模板都被文字拒绝。

在定义 1 中:没有文字的所有模板都被拒绝。此错误通常是由传递命名函数不支持的类型的参数引起的。

[1] 期间:解析被调用者类型:Function()

[2] 期间:键入呼叫 res = np.empty(n, dtype="float32")

文件“thenameofmyscript.py”,第 71 行:

def calc_func(a, b, n):
    res = np.empty(n, dtype="float32")
    ^
Run Code Online (Sandbox Code Playgroud)

@jit("float32:", nopython=False, nogil=True)

thenameofmyscript.py:69: NumbaWarning: 编译正在回退到对象模式而没有启用循环提升,因为函数“calc_func”由于类型推断失败:无法确定 Numba 类型 <class 'numba.dispatcher.LiftedLoop'>

文件“thenameofmyscript.py”,第 73 行:

def calc_func(a, b, n):
        <source elided>
        res[0] = 0
        for i in range(1, n):
        ^
Run Code Online (Sandbox Code Playgroud)

@jit("float32:", nopython=False, nogil=True)

H:\projects\decay-optimizer\venv\lib\site-packages\numba\compiler.py:742: NumbaWarning: 函数“calc_func”是在没有 forceobj=True 的情况下在对象模式下编译的,但已经解除了循环。

文件“thenameofmyscript.py”,第 70 行:

@jit("float32[:](float32,float32,intp)", nopython=False, nogil=True)
    def calc_func(a, b, n):
    ^
Run Code Online (Sandbox Code Playgroud)

self.func_ir.loc))

H:\projects\decay-optimizer\venv\lib\site-packages\numba\compiler.py:751: NumbaDeprecationWarning: 检测到从 nopython 编译路径回退到对象模式编译路径,这是不推荐的行为。

文件“thenameofmyscript.py”,第 70 行:

@jit("float32[:](float32,float32,intp)", nopython=False, nogil=True)
    def calc_func(a, b, n):
    ^
Run Code Online (Sandbox Code Playgroud)

警告。警告(错误。NumbaDeprecationWarning(味精,self.func_ir.loc))

thenameofmyscript.py:69: NumbaWarning: 尽管 nogil=True,但在对象模式下运行的代码不允许并行执行。@jit("float32:", nopython=False, nogil=True)

max*_*111 6

1.优化函数(代数化简)

\n\n

现代 CPU 的加法、减法和乘法运算速度相当快。应尽可能避免诸如求幂之类的运算。

\n\n

例子

\n\n

在这个例子中,我用简单的乘法代替了昂贵的乘方。像这样的简化可以带来相当高的加速,但也可能改变结果。

\n\n

首先,您的实现(float64)没有任何签名,稍后我将在另一个简单的示例中对此进行处理。

\n\n
#nb.jit(nopython=True) is a shortcut for @nb.njit()\n@nb.njit()\ndef calc_func_opt_1(a, b, n):\n    res = np.empty(n, dtype=np.float64)\n    fact=b\n    res[0] = 0.\n    res[1] = a * res[0] + (1. - a) *1.\n    res[2] = a * res[1] + (1. - a) * fact\n    for i in range(3, n):\n        fact*=b\n        res[i] = a * res[i - 1] + (1. - a) * fact\n    return res\n
Run Code Online (Sandbox Code Playgroud)\n\n

另外一个好主意是尽可能使用标量。

\n\n
@nb.njit()\ndef calc_func_opt_2(a, b, n):\n    res = np.empty(n, dtype=np.float64)\n    fact_1=b\n    fact_2=0.\n    res[0] = fact_2\n    fact_2=a * fact_2 + (1. - a) *1.\n    res[1] = fact_2\n    fact_2 = a * fact_2 + (1. - a) * fact_1\n    res[2]=fact_2\n    for i in range(3, n):\n        fact_1*=b\n        fact_2= a * fact_2 + (1. - a) * fact_1\n        res[i] = fact_2\n    return res\n
Run Code Online (Sandbox Code Playgroud)\n\n

时间安排

\n\n
%timeit a = calc_func(0.988, 0.9988, 5000)\n222 \xc2\xb5s \xc2\xb1 2.2 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n%timeit a = calc_func_opt_1(0.988, 0.9988, 5000)\n22.7 \xc2\xb5s \xc2\xb1 45.5 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 10000 loops each)\n%timeit a = calc_func_opt_2(0.988, 0.9988, 5000)\n15.3 \xc2\xb5s \xc2\xb1 35.6 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

2. 签名是否值得推荐?

\n\n

在提前模式 (AOT) 中,签名是必需的,但在通常的 JIT 模式中则不需要。上面的示例不是 SIMD 可矢量化的。因此,您不会看到可能不是最佳的输入和输出声明带来的太多积极或消极影响。\n让我们看另一个例子。

\n\n
#Numba is able to SIMD-vectorize this loop if \n#a,b,res are contigous arrays\n@nb.njit(fastmath=True)\ndef some_function_1(a,b):\n    res=np.empty_like(a)\n    for i in range(a.shape[0]):\n        res[i]=a[i]**2+b[i]**2\n    return res\n\n@nb.njit("float64[:](float64[:],float64[:])",fastmath=True)\ndef some_function_2(a,b):\n    res=np.empty_like(a)\n    for i in range(a.shape[0]):\n        res[i]=a[i]**2+b[i]**2\n    return res\n\na=np.random.rand(10_000)\nb=np.random.rand(10_000)\n\n#Example for non contiguous input\n#a=np.random.rand(10_000)[0::2]\n#b=np.random.rand(10_000)[0::2]\n\n%timeit res=some_function_1(a,b)\n5.59 \xc2\xb5s \xc2\xb1 36.1 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n%timeit res=some_function_2(a,b)\n9.36 \xc2\xb5s \xc2\xb1 47.1 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

为什么带签名的版本速度慢?

\n\n

让我们仔细看看签名。

\n\n
some_function_1.nopython_signatures\n#[(array(float64, 1d, C), array(float64, 1d, C)) -> array(float64, 1d, C)]\nsome_function_2.nopython_signatures\n#[(array(float64, 1d, A), array(float64, 1d, A)) -> array(float64, 1d, A)]\n#this is equivivalent to \n#"float64[::1](float64[::1],float64[::1])"\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果编译时内存布局未知,则通常无法对算法进行 SIMD 向量化。当然,您可以显式声明 C 连续数组,但该函数将不再适用于非连续输入,这通常不是预期的。

\n