即时编译

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 模块中定义。

nopythonnogil 是布尔标志。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’ 会导致除以零时将结果设置为 +/-infnan

并非所有函数都可以被缓存,因为某些功能无法始终持久化到磁盘。当一个函数无法被缓存时,会发出警告。

如果为真,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)

返回一个字典,将编译后的函数签名映射到函数的控制流图对象。如果指定了签名关键字,则返回与该单个签名对应的字符串。

控制流图对象可以通过字符串化(strrepr)来获取图形的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。可选的 nopythonforceobjlocals 参数与 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 会将异常传递给调用者。

参见

用户指南中的“动态通用函数”部分展示了 DUFunc 的调用时行为,并讨论了调用顺序对 Numba 生成底层 ufunc 的影响。

ufunc

实际由 DUFunc 实例构建的 NumPy ufunc 对象。请注意,DUFunc 对象维护了几个重要的数据结构,这些结构对于正确的 ufunc 功能(特别是动态编译的循环)是必需的。用户不应在确保底层 DUFunc 不会被垃圾回收之前传递 ufunc 值。

nin

DUFunc (ufunc) 输入的数量。参见 ufunc.nin

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),其中 aA 中,bB 中。参见 ufunc.outer

at(A, indices, *, B)

对操作数 A 中由 indices 指定的元素执行无缓冲的就地操作。如果你使用的是 NumPy 1.7 或更早的版本,此方法将不存在。请参阅 ufunc.at

备注

矢量化函数在极少数情况下可能会显示 意外的警告或错误

C 回调

@numba.cfunc(signature, nopython=False, cache=False, locals={})

即时编译装饰函数以生成高效的机器码。编译后的代码被封装在一个薄的C回调中,使其可以使用自然的C ABI调用。

签名 是一个代表C回调签名的单一签名。它必须与 jit() 中的形式相同。装饰器不会检查签名中的类型在C中是否有明确定义的表示。

nopythoncache 是布尔标志。locals 是局部变量名到 类型和签名 的映射。它们在 jit() 中的含义相同。

装饰器返回一个 CFunc 对象。

备注

C 回调当前不支持 对象模式

class CFunc

cfunc() 创建的对象类。CFunc 对象暴露了以下属性和方法:

address

编译后的C回调函数的地址,以整数形式表示。

cffi

一个 cffi 函数指针实例,作为参数传递给 cffi 包装的函数。指针的类型是 void *,因此在传递给 cffi 时只会进行最小的类型检查。

ctypes

一个 ctypes 回调实例,就像使用 ctypes.CFUNCTYPE() 创建的那样。

native_name

编译后的C回调函数的名称。

inspect_llvm()

返回为C回调生成的可读的LLVM IR。native_name 是此回调在IR中定义的名称。