A.7 用Numba编写快速NumPy函数

    为了介绍Numba,来考虑一个纯粹的Python函数,它使用for循环计算表达式(x - y).mean():

    这个函数很慢:

    1. In [210]: y = np.random.randn(10000000)
    2. In [211]: %timeit mean_distance(x, y)
    3. 1 loop, best of 3: 2 s per loop
    4. In [212]: %timeit (x - y).mean()

    NumPy的版本要比它快过100倍。我们可以转换这个函数为编译的Numba函数,使用numba.jit函数:

    1. @nb.jit
    2. nx = len(x)
    3. result = 0.0
    4. count = 0
    5. for i in range(nx):
    6. result += x[i] - y[i]
    7. count += 1
    8. 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。

    1. def mean_distance(x, y):
    2. return (x - y).mean()

    我建议你学习Numba的线上文档( ufunc对象的例子。

    numba.vectorize创建了一个编译的NumPy ufunc,它与内置的ufunc很像。考虑一个numpy.add的Python例子:

    现在有:

    1. In [13]: x = np.arange(10)
    2. In [14]: nb_add(x, x)
    3. Out[14]: array([ 0., 2., 4., 6., 8., 10., 12., 14., 16., 18.])
    4. Out[15]: array([ 0., 1., 3., 6., 10., 15., 21., 28., 36., 45.])