网址:http://numba.pydata.org/numba-doc/latest/user/index.html
一、5分钟quick start
Numba是一个python的即时编译器,其使用Numpy的arrays,functions和loops。当调用Numba修饰函数时,它被编译为机器代码即时执行,并且全部或部分代码随后可以以本机机器代码速度运行!(大概意思是部分代码给编译了,不是像之前一样解释运行)
注意:现在numba还在armv7l, armv8l (aarch64)平台上实验,在AMD ROC(GPU)也处于实验状态。
判断什么情况下比较适合使用Numba:
如果代码是使用numpy做数字运算,并且常常有很多的循环,那么使用Numba就是一个很好的选择。
下面是Numba适合和不适合应用场景的示例:
from numba import jit import numpy as np x = np.arange(100).reshape(10, 10) @jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit def go_fast(a): # Function is compiled to machine code when called the first time trace = 0 for i in range(a.shape[0]): # Numba likes loops trace += np.tanh(a[i, i]) # Numba likes NumPy functions return a + trace # Numba likes NumPy broadcasting print(go_fast(x))
从上面的示例可以看出,Numba适合numpy中的循环操作、numpy函数以及broadcasting。
from numba import jit import pandas as pd x = {'a': [1, 2, 3], 'b': [20, 30, 40]} @jit def use_pandas(a): # Function will not benefit from Numba jit df = pd.DataFrame.from_dict(a) # Numba doesn't know about pd.DataFrame df += 1 # Numba doesn't understand what this is return df.cov() # or this! print(use_pandas(x))
从上面这个例子可以看出numba不适合字典型变量和一些非numpy的函数,尤其是上面numba不能解析pandas,上面的函数内容在运行时也就无法编译。
关于nopython模式:
Numba中的@jit运算符能按照两种编译模式运行,nopython模式和object模式。例如上面的go_fast函数,nopython=true,将@jit设置为nopython模式。nopython编译模式的行为本质上是编译装饰函数,以便它完全运行而不需要Python解释器的参与。这是使用Numba jit装饰器的推荐和最佳实践方式,因为它可以带来最佳性能。
如果nopython模式下的编译失败,Numba可以使用对象模式进行编译,如果未设置nopython = True,则这是@jit装饰器的fall back模式(如上面的use_pandas示例中所示)。在这种模式下,Numba将识别它可以编译的循环,并将它们编译成在机器代码中运行的函数,并且它将运行解释器中的其余代码。为获得最佳性能,请避免使用此模式。
如何去衡量Numba的性能:
首先,numba在第一次执行的时候会执行函数编译为机器代码,这是需要耗时的,但是后面numba会将编译好的机器代码缓存起来以供后面的循环调用。
注意一个地方,如果编译时间很长,那么numba是支持将编译好的函数存到disk上的。
Numba是如何工作的:
Numba读取了@jit函数的python字节,并且分析优化代码,最后使用LLVM编译器生成函数的机器代码。
Numba中的decorators:
@njit | @njit(nopython=true)的简称 |
二、安装
安装完成以后,可以使用 numba-s 命令报告整个系统的信息。(是在命令行里面)
三、编译python代码,使用@jit
基本使用:
1、Lazy compilation
使用@jit的推荐方式为使用numba去决定如何去优化:
from numba import jit @jit def f(x, y): # A somewhat trivial example return x + y
在这种模式之下,编译会被推迟到第一次执行这个函数,numba回去推断输入的参数类型,然后据此做出优化。如果第一次调用的时候传入的是整数,而后面某次传入的是复数,会再次编译生成不同的机器代码。
2、Eager compilation
这种方式是在定义函数的时候确定使用什么样的数据类型。
from numba import jit, int32 @jit(int32(int32, int32)) def f(x, y): # A somewhat trivial example return x + y
这种做法是对编译器做出了fine-grained的控制。上面括号外面的int32指的是返回的数据类型,(int32,int32)指的是输入的参数类型。
调用和内联其他函数:
@jit def square(x): return x ** 2 @jit def hypot(x, y): return math.sqrt(square(x) + square(y))
注意上面hypot调用了square这个函数,@jit必须要添加在square这个函数上面,否则的话,会生成更慢的代码。
@jit可以使用的数据类型:
void: 空类型
int8,uint8 ...16...32...64等
intc, uintc 和 int, unsigned int是一样的
intp, uintp是pointer-sized integers (没有搞懂)
float32,float64
complex64,complex128
编译options:
1、nopython:
选择nopython模式或者是object模式
2、nogil
该项设置为true,一旦编译完成,就会释放GIL,这样的情况下就可以充分利用多核系统,但是需要注意多线程编程中需要注意的同步、一致性、竞争等情况。
3、cache
为了避免每次编译所耗费的时间,可以将函数编译完成的结果保存在一个file文件中。
4、parallel
自动并行化,注意需要和nopython=true一起使用
@jit(nopython=True, parallel=True) def f(x, y): return x + y