Numba是Python的即时编译器,可以作用在NumPy数组、函数和循环代码上。使用Numba最常见的方式是通过它的装饰器,将装饰器应用到函数上,指示Numba编译它们。当调用numba修饰函数时,代码将会被“即时”编译为机器码执行,使代码可以以机器码速度运行,极大地提高python代码的运行速度。
$ conda install numba
$ pip install numba
当然,可以通过源码进行编译安装,但不推荐第一次适用Numba的用户使用。
Numba常常被作为核心库使用,所以其依赖库很少,当然,一些额外的功能需要一些可选的依赖库的支持:
scipy
- 以支持编译 numpy.linalg
函数。colorama
- 以支持回溯或错误消息中的颜色高亮。pyyaml
- 以支持通过Yaml文件配置Numba.icc_rt
- 以支持使用英特尔 SVML (高性能短向量数学库, 仅支持x86_64架构). 安装指南参见 performance tips.这取决于你的代码是什么形式的,如果代码主要为数学运算,使用了较多的NumPy计算或者有很多循环操作,那么Numba将会是一个很好的选择。在下面的例子中,将采用Numba最基本的JIT装饰器@jit,加速编写好的一些函数。
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.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的加速效果将不佳:
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不支持Pandas库,因此Numba会简单地通过解释器运行这段代码,而且还会增加Numba内部开销的成本!
nopython
模式?Numba @jit装饰器一般以两种编译模式运行,nopython模式和object模式。在上面的go_fast例子中,@jit装饰器中设置了nopython=True;这是指示Numba在nopython模式下操作。nopython编译模式的行为本质上是编译修饰函数,使其完全运行而不需要Python解释器的参与。这是使用Numba jit装饰器的最佳实践方式,因为它能带来最好的性能。
如果在nopython模式下编译失败,Numba可以使用对象模式编译。如果没有设置nopython=True(如上面的use_pandas例子所示),@jit装饰器将采用回退模式,在这种模式下,Numba将识别它可以编译的循环,并将这些循环编译成在机器码中运行的函数,然后在解释器中运行其余的代码。为了获得最佳性能,应避免使用这种模式!
如何衡量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支持已编译函数的磁盘缓存,并且还支持提前编译模式。
Numba读取修饰函数的Python字节码,并将其与关于函数输入参数类型的信息结合起来。分析和优化代码,最后使用LLVM编译器库生成机器码版本,并兼容当前的CPU,且以后每次调用函数时都会使用编译后的版本。
Numba有相当多的装饰器,前面主要介绍了@jit,但也有:
@njit
- @jit(nopython=True)
的简写,方便快速使用@vectorize
- 用于生成Numpy的 ufunc
函数 (支持所有的 ufunc
方法). Docs are here.@guvectorize
- 用于生成通用的NumPyufunc
函数. Docs are here.@stencil
- 将函数声明为类似于模板的操作的内核. Docs are here.@jitclass
- 可用于编译Python类. Docs are here.@cfunc
- 声明一个函数用于本机回调(从C/ c++等调用). Docs are here.@overload
- 注册一个函数实现以供在nopython模式下使用, 如 @overload(scipy.special.j0)
. Docs are here.一些装饰器提供的额外选项:
parallel = True
- 为函数提供自动并行运行。fastmath = True
- 为函数提供快速的数学运算。ctypes / cffi / cython互操作性:
Numba支持Nvidia CUDA和(实验性的)AMD ROC图形处理器。你可以用纯Python编写一个内核,让Numba处理计算数据。详细可参见CUDA或ROC。
参考:https://numba.readthedocs.io/en/stable/user/5minguide.html
Copyright © 2021 .长沙麦涛网络科技有限公司 All rights reserved.
湘ICP备20015126号-2
联系我们