A.7 用Numba编写快速NumPy函数
为了介绍Numba,来考虑一个纯粹的Python函数,它使用for循环计算表达式(x - y).mean():
这个函数很慢:
In [210]: y = np.random.randn(10000000)
In [211]: %timeit mean_distance(x, y)
1 loop, best of 3: 2 s per loop
In [212]: %timeit (x - y).mean()
NumPy的版本要比它快过100倍。我们可以转换这个函数为编译的Numba函数,使用numba.jit函数:
@nb.jit
nx = len(x)
result = 0.0
count = 0
for i in range(nx):
result += x[i] - y[i]
count += 1
return result / count
它要比矢量化的NumPy快:
Numba不能编译Python代码,但它支持纯Python写的一个部分,可以编写数值算法。
Numba是一个深厚的库,支持多种硬件、编译模式和用户插件。它还可以编译NumPy Python API的一部分,而不用for循环。Numba也可以识别可以便以为机器编码的结构体,但是若调用CPython API,它就不知道如何编译。Numba的jit函数有一个选项,nopython=True,它限制了可以被转换为Python代码的代码,这些代码可以编译为LLVM,但没有任何Python C API调用。jit(nopython=True)有一个简短的别名numba.njit。
def mean_distance(x, y):
return (x - y).mean()
我建议你学习Numba的线上文档( ufunc对象的例子。
numba.vectorize创建了一个编译的NumPy ufunc,它与内置的ufunc很像。考虑一个numpy.add的Python例子:
现在有:
In [13]: x = np.arange(10)
In [14]: nb_add(x, x)
Out[14]: array([ 0., 2., 4., 6., 8., 10., 12., 14., 16., 18.])
Out[15]: array([ 0., 1., 3., 6., 10., 15., 21., 28., 36., 45.])