什么是nopython
模式?
Numba@jit
装饰器基本上以两种编译模式运行, nopython
模式和object
模式。在go_fast
上面的例子中, nopython=True
是在@jit
装饰器中设置的;这是指示 Numba 在nopython
模式下运行。nopython
编译模式的行为本质上是编译装饰函数,使其完全运行而无需 Python 解释器的参与。这是使用 Numbajit
装饰器的推荐和最佳实践方法,因为它可以带来最佳性能。
如果nopython
模式下的编译失败,Numba 可以使用 . 如果未设置,这是装饰器的 回退模式(如上例所示)。在这种模式下,Numba 将识别它可以编译的循环并将它们编译成以机器代码运行的函数,并且它将在解释器中运行其余的代码。为获得最佳性能,请避免使用此模式!object mode
@jit
nopython=True
use_pandas
如何衡量Numba的性能?
首先,回想一下,Numba 在执行函数的机器代码版本之前必须针对给定的参数类型编译函数。这需要时间。但是,一旦编译发生,Numba 就会缓存您的函数的机器代码版本,用于提供的特定类型的参数。如果用相同的类型再次调用它,它可以重用缓存的版本,而不必再次编译。
测量性能时一个非常常见的错误是不考虑上述行为,并使用一个简单的计时器对代码进行一次计时,该计时器包括在执行时间内编译函数所花费的时间。
例如:
from numba import jit
import numpy as np
import time
x = np.arange(100).reshape(10, 10)
@jit(nopython=True)
def go_fast(a): # Function is compiled and runs in machine code
trace = 0.0
for i in range(a.shape[0]):
trace += np.tanh(a[i, i])
return a + trace
# DO NOT REPORT THIS... COMPILATION TIME IS INCLUDED IN THE EXECUTION TIME!
start = time.time()
go_fast(x)
end = time.time()
print("Elapsed (with compilation) = %s" % (end - start))
# NOW THE FUNCTION IS COMPILED, RE-TIME IT EXECUTING FROM CACHE
start = time.time()
go_fast(x)
end = time.time()
print("Elapsed (after compilation) = %s" % (end - start))
例如,这会打印:
Elapsed (with compilation) = 0.33030009269714355
Elapsed (after compilation) = 6.67572021484375e-06
衡量 Numba JIT 对代码影响的一个好方法是使用timeit模块函数来计时;这些测量执行的多次迭代,因此,可以在第一次执行时适应编译时间。
附带说明一下,如果编译时间是一个问题,Numba JIT 支持 已编译函数的磁盘缓存,并且还具有Ahead-Of-Time编译模式。
它有多快?
假设 Numba 可以在nopython
mode 下运行,或者至少编译一些循环,它将针对您的特定 CPU 进行编译。加速因应用而异,但可以是一到两个数量级。Numba 有一个 性能指南,涵盖了获得额外性能的常见选项。
Numba 是如何工作的?
Numba 读取装饰函数的 Python 字节码,并将其与有关函数输入参数类型的信息结合起来。它会分析和优化您的代码,最后使用 LLVM 编译器库为您的函数生成机器代码版本,并根据您的 CPU 功能量身定制。每次调用您的函数时都会使用此编译版本。
其他感兴趣的东西:
Numba 有很多装饰器,我们已经看到了@jit
,但也有:
-
@njit
- 这是一个别名,@jit(nopython=True)
因为它是如此常用! -
@vectorize
- 生成 NumPyufunc
s(ufunc
支持所有方法)。文档在这里。 -
@guvectorize
- 产生 NumPy 广义ufunc
s。 文档在这里。 -
@stencil
- 将函数声明为类似模板的操作的内核。 文档在这里。 -
@jitclass
- 用于 jit 感知类。文档在这里。 -
@cfunc
- 声明一个用作本机回调的函数(从 C/C++ 等调用)。文档在这里。 -
@overload
- 注册您自己的函数实现以在 nopython 模式下使用,例如@overload(scipy.special.j0)
. 文档在这里。
一些装饰器中可用的额外选项:
ctypes/cffi/cython 互操作性:
GPU目标:
Numba 可以针对Nvidia CUDA和(实验性)AMD ROC GPU。您可以使用纯 Python 编写内核并让 Numba 处理计算和数据移动(或明确执行此操作)。单击有关CUDA或ROC 的Numba 文档 。
参考文献:https://numba.readthedocs.io/en/stable/user/5minguide.html