【发布时间】:2020-07-23 19:20:06
【问题描述】:
这是一个人为的测试用例,但希望它足以传达要点并提出问题。在 Numba njit 函数内部,我注意到将本地计算的值分配给数组元素非常昂贵。以下是两个示例函数:
from numba import njit
import numpy as np
@njit
def slow_func(x, y):
result = y.sum()
for i in range(x.shape[0]):
if x[i] > result:
x[i] = result
else:
x[i] = result
@njit
def fast_func(x, y):
result = y.sum()
for i in range(x.shape[0]):
if x[i] > result:
z = result
else:
z = result
if __name__ == "__main__":
x = np.random.rand(100_000_000)
y = np.random.rand(100_000_000)
%timeit slow_func(x, y) # 177 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit fast_func(x, y) # 407 ns ± 12.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
我知道这两个函数做的事情并不完全一样,但我们暂时不用担心,继续专注于“慢任务”。此外,由于 Numba 的延迟初始化,上述时间已在 JIT 编译后重新运行。请注意,这两个函数都将result 分配给x[i] 或z,并且在这两种情况下分配的数量相同。但是,将result 分配给z 的速度要快得多。有没有办法让slow_func 和fast_func 一样快?
【问题讨论】:
-
不是编译器专家,但如果您的大多数示例函数都得到优化,我不会感到惊讶。如今,编译器非常聪明。特别是,分配给
z没有任何效果,因此可能会被 jit 丢弃。 -
我刚刚将您的
fast_func与一个什么都不做并返回None的函数进行了比较。它们的执行时间相同。 -
@PaulPanzer 我认为你可能是对的。如果在
fast_func的末尾简单地返回z,则时间与slow_func大致相同。尽管如此,我没想到数组分配会这么慢