加速python运行-numba
numba是一个用于编译Python数组和数值计算函数的编译器,这个编译器能够大幅提高直接使用Python编写的函数的运算速度。
numba使用LLVM编译器架构将纯Python代码生成优化过的机器码,通过一些添加简单的注解,将面向数组和使用大量数学的python代码优化到与c,c++和Fortran类似的性能,而无需改变Python的解释器。
Numba的主要特性:
动态代码生成 (在用户偏爱的导入期和运行期)
为CPU(默认)和GPU硬件生成原生的代码
集成Python的科学软件栈(Numpy)
下面是使用Numba优化的函数方法,将Numpy数组作为参数:
import numba
@numba.jit
def sum2d(arr):
M, N = arr.shape
result = 0.0
for i in range(M):
for j in range(N):
result += arr[i,j]
return result
如果你对此不是太感兴趣,或者对于其他的加速方案已经很熟悉,可以到此为止,只需要了解加上jit装饰器就可以实现了。
使用jit
使用jit的好处就在于让numba来决定什么时候以及怎么做优化。
from numba import jit
@jit
def f(x, y):
# A somewhat trivial example
return x + y
比如这段代码,计算将延期到第一次函数执行,numba将在调用期间推断参数类型,然后基于这个信息生成优化后的代码。numba也能够基于输入的类型编译生成特定的代码。例如,对于上面的代码,传入整数和复数作为参数将会生成不同的代码:
>>>f(1,2)
3
>>>f(1j,2)
(2+1j)
我们也可以加上所期望的函数签名:
from numba import jit, int32
@jit(int32(int32, int32))
def f(x, y):
# A somewhat trivial example
return x + y
int32(int32, int32) 是函数签名,这样,相应的特性将会被@jit装饰器编译,然后,编译器将控制类型选择,并不允许其他特性(即其他类型的参数输入,如float)
Numba编译的函数可以调用其他编译函数。 函数调用甚至可以在本机代码中内联,具体取决于优化器的启发式。 例如:
@jit
def square(x):
return x ** 2
@jit
def hypot(x, y):
return math.sqrt(square(x) + square(y))
@jit装饰器必须添加到任何库函数,否则numba可能生成速度更慢的代码。
签名规范
Explicit @jit signatures can use a number of types. Here are some common ones:
void is the return type of functions returning nothing (which actually return None when called from Python)
intp and uintp are pointer-sized integers (signed and unsigned, respectively)
intc and uintc are equivalent to C int and unsigned int integer types
int8, uint8, int16, uint16, int32, uint32, int64, uint64 are fixed-width integers of the corresponding bit width (signed and unsigned)
float32 and float64 are single- and double-precision floating-point numbers, respectively
complex64 and complex128 are single- and double-precision complex numbers, respectively
array types can be specified by indexing any numeric type, e.g. float32[:] for a one-dimensional single-precision array or int8[:,:] for a two-dimensional array of 8-bit integers.
编译选项
numba有两种编译模式:nopython模式和object模式。前者能够生成更快的代码,但是有一些限制可能迫使numba退为后者。想要避免退为后者,而且抛出异常,可以传递nopython=True.
@jit(nopython=True)
def f(x, y):
return x + y
当Numba不需要保持全局线程锁时,如果用户设定nogil=True,当进入这类编译好的函数时,Numba将会释放全局线程锁。
@jit(nogil=True)