即时编译
JIT 函数
- @numba.jit(signature=None, nopython=False, nogil=False, cache=False, forceobj=False, parallel=False, error_model='python', fastmath=False, locals={}, boundscheck=False)
即时编译装饰函数以生成高效的机器码。所有参数都是可选的。
如果存在,签名 可以是单个签名或代表函数参数和返回值预期 类型和签名 的签名列表。每个签名可以以多种形式给出:
一个 类型和签名 参数的元组(例如
(numba.int32, numba.double)
),表示函数参数的类型;Numba 将从这些参数中推断出合适的返回类型。使用 类型和签名 的调用签名,指定返回类型和参数类型。这可以用直观的形式给出(例如
numba.void(numba.int32, numba.double)
)。上述之一的字符串表示形式,例如
"void(int32, double)"
。字符串中使用的所有类型名称都假定在numba.types
模块中定义。
nopython 和 nogil 是布尔标志。locals 是一个将局部变量名称映射到 类型和签名 的映射。
这个装饰器有几种操作模式:
如果在 signature 中给出一个或多个签名,则会为每个签名编译一个特化。调用装饰后的函数将尝试选择最佳匹配的签名,如果没有合适的转换可用,则会引发
TypeError
。如果转换成功,编译后的机器代码将使用转换后的参数执行,并根据签名将返回值转换回来。如果没有给出 签名 ,装饰的函数将实现惰性编译。每次调用装饰的函数时,如果存在现有的特化(例如,调用两个整数参数可能会重用参数类型
(numba.int64, numba.int64)
的特化),则会尝试重用该特化。如果没有合适的特化存在,则会即时编译一个新的特化,存储以供以后使用,并使用转换后的参数执行。
如果为真,nopython 强制函数在 nopython 模式 下编译。如果不可能,编译将引发错误。
如果为真,forceobj 强制函数以 对象模式 编译。由于对象模式比 nopython 模式慢,这主要用于测试目的。
如果为真,nogil 会尝试在编译的函数内部释放 全局解释器锁。只有在 Numba 能够在 nopython 模式 下编译函数时,GIL 才会被释放,否则会打印一个编译警告。
如果为真,缓存 启用一个基于文件的缓存,以缩短函数在先前调用中已经编译时的编译时间。缓存保存在包含源文件的目录的
__pycache__
子目录中;然而,如果当前用户无权写入该目录,则会回退到特定于平台的用户范围的缓存目录(例如,在Unix平台上为$HOME/.cache/numba
)。如果为真,parallel 将启用对许多常见 NumPy 构造的自动并行化,以及相邻并行操作的融合,以最大化缓存局部性。
error_model
选项控制除以零的行为。将其设置为 ‘python’ 会导致除以零时引发异常,类似于 CPython。将其设置为 ‘numpy’ 会导致除以零时将结果设置为 +/-inf 或 nan。并非所有函数都可以被缓存,因为某些功能无法始终持久化到磁盘。当一个函数无法被缓存时,会发出警告。
如果为真,fastmath 将启用使用在 LLVM 文档 中描述的否则不安全的浮点变换。此外,如果安装了 Intel SVML,将使用一些数学内在函数的更快但不太精确的版本(答案在
4 ULP
以内)。如果为真,boundscheck 会为数组索引启用边界检查。越界访问将引发 IndexError。默认情况下不进行边界检查。如果禁用边界检查,越界访问可能会产生垃圾结果或导致段错误。然而,启用边界检查会减慢典型函数的执行速度,因此建议仅在调试时使用此标志。您还可以将 NUMBA_BOUNDSCHECK 环境变量设置为 0 或 1 以全局覆盖此标志。
可以使用 locals 字典来强制指定特定局部变量的 类型和签名,例如,如果你想在某些地方强制使用单精度浮点数。通常情况下,我们建议你让 Numba 的编译器自行推断局部变量的类型。
以下是一个包含两个签名的示例:
@jit(["int32(int32)", "float32(float32)"], nopython=True) def f(x): ...
在装饰器后面不加任何括号等同于不带任何参数调用装饰器,即:
@jit def f(x): ...
相当于:
@jit() def f(x): ...
装饰器返回一个
Dispatcher
对象。备注
如果没有给出 签名 ,在实际编译发生时,即函数首次被调用并传入某些参数类型时,将会引发编译错误。
备注
编译可以受到一些专门的 环境变量 的影响。
生成的JIT函数
类似于 jit()
装饰器,但在编译时调用被装饰的函数,传递函数的参数的 类型 。被装饰的函数必须返回一个可调用对象,该对象将被编译为这些类型的函数的实现,从而允许灵活的特化。
如果你在寻找此功能,请参阅 高级扩展API @overload
系列的装饰器。
调度器对象
- class Dispatcher
通过调用
jit()
创建的对象类。你不应该尝试以任何其他方式创建此类对象。调用 Dispatcher 对象会调用为其调用参数编译的专用版本,使其作为已编译的 Python 函数的加速替代品。此外,Dispatcher 对象具有以下方法和属性:
- py_func
已编译的纯Python函数。
- inspect_types(file=None, pretty=False)
打印出带有相应 Numba IR 注释的函数源代码列表,以及各种变量的推断类型。如果指定了 file,则打印到该文件对象,否则打印到 sys.stdout。如果 pretty 设置为 True,则在终端中生成彩色 ANSI,在笔记本中生成 HTML。
参见
架构
- inspect_llvm(signature=None)
返回一个字典,将编译后的函数签名映射到为该函数生成的人类可读的LLVM IR。如果指定了签名关键字,则返回与该单个签名对应的字符串。
- inspect_asm(signature=None)
返回一个字典,将编译后的函数签名映射到该函数的人类可读的本地汇编代码。如果指定了签名关键字,则返回与该单个签名对应的字符串。
- inspect_cfg(signature=None, show_wrapped)
返回一个字典,将编译后的函数签名映射到函数的控制流图对象。如果指定了签名关键字,则返回与该单个签名对应的字符串。
控制流图对象可以通过字符串化(
str
或repr
)来获取图形的DOT格式文本表示。或者,使用其.display(filename=None, view=False)
方法来绘制图形。filename 选项可以设置为渲染输出的特定路径。如果 view 选项为 True,图形将由系统默认的图像格式(PDF)应用程序打开。在 IPython 笔记本中,返回的对象可以内嵌绘制。用法:
@jit def foo(): ... # opens the CFG in system default application foo.inspect_cfg(foo.signatures[0]).display(view=True)
- inspect_disasm_cfg(signature=None)
返回一个字典,将编译后的函数签名映射到底层编译的
ELF
对象的反汇编控制流图。如果指定了签名关键字,则返回与该单个签名对应的控制流图。此函数具有执行环境感知能力,将在 Jupyter 笔记本中生成 SVG 输出,在终端中生成 ASCII 输出。示例:
@njit def foo(x): if x < 3: return x + 1 return x + 2 foo(10) print(foo.inspect_disasm_cfg(signature=foo.signatures[0]))
给出:
[0x08000040]> # method.__main__.foo_241_long_long (int64_t arg1, int64_t arg3); ─────────────────────────────────────────────────────────────────────┐ │ 0x8000040 │ │ ; arg3 ; [02] -r-x section size 279 named .text │ │ ;-- section..text: │ │ ;-- .text: │ │ ;-- __main__::foo$241(long long): │ │ ;-- rip: │ │ 25: method.__main__.foo_241_long_long (int64_t arg1, int64_t arg3); │ │ ; arg int64_t arg1 @ rdi │ │ ; arg int64_t arg3 @ rdx │ │ ; 2 │ │ cmp rdx, 2 │ │ jg 0x800004f │ └─────────────────────────────────────────────────────────────────────┘ f t │ │ │ └──────────────────────────────┐ └──┐ │ │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ 0x8000046 │ │ 0x800004f │ │ ; arg3 │ │ ; arg3 │ │ inc rdx │ │ add rdx, 2 │ │ ; arg3 │ │ ; arg3 │ │ mov qword [rdi], rdx │ │ mov qword [rdi], rdx │ │ xor eax, eax │ │ xor eax, eax │ │ ret │ │ ret │ └─────────────────────────┘ └─────────────────────────┘
- recompile()
重新编译所有现有的签名。例如,如果一个全局或闭包变量被你的函数冻结,并且它在Python中的值发生了变化,这可能会很有用。由于编译不是廉价的操作,这主要用于测试和交互式使用。
- parallel_diagnostics(signature=None, level=1)
打印给定签名的并行诊断信息。如果没有提供签名,则打印所有已知签名的信息。
level
用于调整详细程度,``level=1``(默认)是最小详细程度,级别 2、3 和 4 提供递增的详细程度。
- get_metadata(signature=None)
获取给定签名的编译元数据。这对于Numba和Numba扩展的开发者非常有用。
矢量化函数(ufuncs 和 DUFuncs)
- @numba.vectorize(*, signatures=[], identity=None, nopython=True, target='cpu', forceobj=False, cache=False, locals={})
编译装饰函数并将其包装为 NumPy ufunc 或 Numba
DUFunc
。可选的 nopython、forceobj 和 locals 参数与numba.jit()
中的含义相同。signatures 是一个可选的签名列表,其形式与
numba.jit()
signature 参数中的形式相同。如果 signatures 非空,那么装饰器将把用户Python函数编译成一个NumPy ufunc。如果没有给出 signatures,那么装饰器将把用户Python函数包装在一个DUFunc
实例中,该实例将在调用时编译用户函数,每当NumPy无法为输入参数找到匹配的循环时。如果 target 是"parallel"
,则需要 signatures。identity 是正在实现的函数的标识(或单位)值。可能的值是 0、1、None 和字符串
"reorderable"
。默认值是 None。None 和"reorderable"
都表示函数没有标识值;"reorderable"
还指定可以重新排序多个轴上的归约。如果有多个 签名 ,它们必须从更具体的到最不具体的顺序排列。否则,NumPy 基于类型的分派可能不会按预期工作。例如,以下是错误的:
@vectorize(["float64(float64)", "float32(float32)"]) def f(x): ...
如果在单精度数组上运行,会选择编译函数的
float64
版本,导致执行效率大大降低。正确的调用方式是:@vectorize(["float32(float32)", "float64(float64)"]) def f(x): ...
target 是一个用于后端目标的字符串;可用值为 “cpu”、”parallel” 和 “cuda”。要使用多线程版本,请将目标更改为 “parallel”(这需要指定签名):
@vectorize(["float64(float64)", "float32(float32)"], target='parallel') def f(x): ...
对于 CUDA 目标,使用 “cuda”:
@vectorize(["float64(float64)", "float32(float32)"], target='cuda') def f(x): ...
编译后的函数可以被缓存以减少未来的编译时间。通过将 cache 设置为 True 来启用此功能。只有 “cpu” 和 “parallel” 目标支持缓存。
此函数创建的 ufuncs 遵循 NEP-13,这是 NumPy 用于覆盖 ufuncs 的机制。如果 ufunc 的
__call__
的任何参数具有__array_ufunc__
方法,则将调用该方法(在 Python 中,而不是编译的上下文中),该方法可能会预处理和/或后处理编译的 ufunc 的参数和返回值(或者可能根本不调用它)。
- @numba.guvectorize(signatures, layout, *, identity=None, nopython=True, target='cpu', forceobj=False, cache=False, locals={})
广义版本的
numba.vectorize()
。虽然numba.vectorize()
会生成一个简单的 ufunc,其核心功能(你正在装饰的函数)对标量操作数进行操作并返回标量值,但numba.guvectorize()
允许你创建一个 NumPy ufunc ,其核心函数接受各种维度的数组参数。额外的参数 layout 是一个字符串,以符号形式指定参数类型和返回类型之间的维度和大小关系。例如,矩阵乘法将有一个布局字符串
"(m,n),(n,p)->(m,p)"
。它的定义可能是(省略函数体):@guvectorize(["void(float64[:,:], float64[:,:], float64[:,:])"], "(m,n),(n,p)->(m,p)") def f(a, b, result): """Fill-in *result* matrix such as result := a * b""" ...
如果其中一个参数应该是标量,相应的布局规范是
()
,该参数实际上会以零维数组的形式传递给你(你需要对其解引用以获取标量值)。例如,一个带有可参数化窗口宽度的 一维移动平均 可能有一个布局字符串"(n),()->(n)"
。请注意,任何输出都将作为额外的函数参数预先分配给你:你的代码必须用你正在实现的函数的适当值来填充它。
如果你的函数不接受输出数组,你应该在布局字符串中省略“箭头”(例如
"(n),(n)"
)。这样做时,重要的是要注意,对输入数组的更改并不总是可以依赖于在ufunc执行之外可见,因为NumPy可能会将临时数组作为输入传递(例如,如果需要进行类型转换)。参见
NumPy 支持的 layout string 规范。请注意,NumPy 使用术语“签名”,而我们不幸地将其用于其他用途。
编译后的函数可以被缓存以减少未来的编译时间。通过将 cache 设置为 True 来启用此功能。只有 “cpu” 和 “parallel” 目标支持缓存。
- class numba.DUFunc
通过调用
numba.vectorize()
创建的对象类,不带签名。DUFunc 实例的行为应与 NumPy
ufunc
对象相似,但有一个重要区别:调用时循环生成。当调用 ufunc 时,NumPy 会查看为该 ufunc 注册的现有循环,如果找不到可以安全转换输入的循环,则会引发TypeError
。当调用 DUFunc 时,Numba 将调用委托给 NumPy。如果 NumPy ufunc 调用失败,那么 Numba 会尝试为给定的输入类型构建一个新的循环,并再次调用 ufunc。如果第二次调用尝试失败或发生编译错误,则 DUFunc 会将异常传递给调用者。- ufunc
实际由
DUFunc
实例构建的 NumPyufunc
对象。请注意,DUFunc
对象维护了几个重要的数据结构,这些结构对于正确的 ufunc 功能(特别是动态编译的循环)是必需的。用户不应在确保底层DUFunc
不会被垃圾回收之前传递ufunc
值。
- nout
DUFunc 输出的数量。参见 ufunc.nout。
- nargs
可能的 DUFunc 参数总数(应为
nin
+nout
)。参见 ufunc.nargs。
- ntypes
DUFunc 支持的输入类型数量。参见 ufunc.ntypes。
- types
支持的类型列表,以字符串形式给出。参见 ufunc.types。
- identity
在使用ufunc作为归约时的标识值。参见 ufunc.identity。
- reduce(A, *, axis, dtype, out, keepdims)
通过沿一个轴应用 DUFunc 来将 A 的维度减少一。参见 ufunc.reduce。
- accumulate(A, *, axis, dtype, out)
累加应用操作符到所有元素的结果。参见 ufunc.accumulate。
- reduceat(A, indices, *, axis, dtype, out)
在单个轴上使用指定的切片执行(局部)归约。参见 ufunc.reduceat。
- outer(A, B)
将 ufunc 应用于所有配对 (a, b),其中 a 在 A 中,b 在 B 中。参见 ufunc.outer。
备注
矢量化函数在极少数情况下可能会显示 意外的警告或错误。
C 回调
- @numba.cfunc(signature, nopython=False, cache=False, locals={})
即时编译装饰函数以生成高效的机器码。编译后的代码被封装在一个薄的C回调中,使其可以使用自然的C ABI调用。
签名 是一个代表C回调签名的单一签名。它必须与
jit()
中的形式相同。装饰器不会检查签名中的类型在C中是否有明确定义的表示。nopython 和 cache 是布尔标志。locals 是局部变量名到 类型和签名 的映射。它们在
jit()
中的含义相同。装饰器返回一个
CFunc
对象。备注
C 回调当前不支持 对象模式。
- class CFunc
由
cfunc()
创建的对象类。CFunc
对象暴露了以下属性和方法:- address
编译后的C回调函数的地址,以整数形式表示。
- ctypes
一个
ctypes
回调实例,就像使用ctypes.CFUNCTYPE()
创建的那样。
- native_name
编译后的C回调函数的名称。
- inspect_llvm()
返回为C回调生成的可读的LLVM IR。
native_name
是此回调在IR中定义的名称。