2.2。即时编译

原文: http://numba.pydata.org/numba-doc/latest/reference/jit-compilation.html

2.2.1。 JIT 函数

  1. @numba.jit(signature=None, nopython=False, nogil=False, cache=False, forceobj=False, parallel=False, error_model='python', fastmath=False, locals={})

即时编译修饰函数以生成高效的机器代码。所有参数都是可选的。

如果存在, 签名 是单个签名或表示函数参数和返回值的预期类型和签名的签名列表。每个签名可以以多种形式给出:

  • 类型和签名参数(例如(numba.int32, numba.double))的元组,表示函数参数的类型;然后,Numba 将从参数中推断出适当的返回类型。
  • 使用类型和签名的呼叫签名,指定返回类型和参数类型。这可以以直观的形式给出(例如numba.void(numba.int32, numba.double))。
  • 上述之一的字符串表示,例如"void(int32, double)"。假定在numba.types模块中定义字符串中使用的所有类型名称。

nopythonnogil 是布尔标志。 locals 是局部变量名称到类型和签名的映射。

这个装饰器有几种操作模式:

  • 如果在 签名 中给出一个或多个签名,则为它们中的每一个编译专门化。然后调用修饰函数将尝试选择最佳匹配签名,如果函数参数没有适当的转换,则引发 TypeError 。如果转换成功,则使用转换的参数执行编译的机器代码,并根据签名转换返回值。
  • 如果没有给出 签名 ,则修饰函数实现延迟编译。每次对装饰函数的调用都会尝试重用现有的特化(如果存在)(例如,带有两个整数参数的调用可能会重用参数类型(numba.int64, numba.int64)的特化)。如果不存在合适的特化,则会即时编译新的特化,存储以供以后使用,并使用转换的参数执行。

如果为 true, nopython 强制在 nopython 模式中编译该函数。如果不可能,编译将引发错误。

如果为真, forceobj 强制在对象模式中编译该函数。由于对象模式比 nopython 模式慢,因此这对于测试目的非常有用。

如果为 true, nogil 会尝试在编译函数内释放全局解释器锁。只有当 Numba 能够在 nopython 模式中编译函数时才会释放 GIL,否则将打印编译警告。

如果为 true,则 cache 允许基于文件的缓存在前一次调用中编译该函数时缩短编译时间。缓存保存在包含源文件的目录的__pycache__子目录中;但是,如果不允许当前用户写入它,则它会回退到特定于平台的用户范围的缓存目录(例如 Unix 平台上的$HOME/.cache/numba)。

如果为真,则 parallel 启用多个常见 Numpy 构造的自动并行化以及相邻并行操作的融合,以最大化缓存局部性。

error_model 选项控制被零除的行为。将它设置为’python’会导致被零除以引发像 CPython 这样的异常。将其设置为’numpy’会导致被零除以将结果设置为 +/- infnan

并非所有函数都可以缓存,因为某些功能无法始终保留到磁盘。当无法缓存函数时,会发出警告;使用 NUMBA_WARNINGS 查看。

如果为真, fastmath 允许使用 LLVM 文档中描述的其他不安全的浮点变换。此外,如果英特尔 SVML 的安装速度更快,但使用的数学内在函数的版本不太准确(4 ULP内的答案)。

locals 字典可用于强制特定局部变量的类型和签名,例如,如果您想在某个时刻强制使用单精度浮点数。通常,我们建议您让 Numba 的编译器自己推断局部变量的类型。

以下是两个签名的示例:

  1. @jit(["int32(int32)", "float32(float32)"], nopython=True)
  2. def f(x): ...

在装饰器之后没有放任何括号等同于在没有任何参数的情况下调用装饰器,即:

  1. @jit
  2. def f(x): ...

相当于:

  1. @jit()
  2. def f(x): ...

装饰器返回 Dispatcher 对象。

注意

如果没有给出 签名 ,则在实际编译发生时,即首次使用某些给定的参数类型调用函数时,将引发编译错误。

注意

编译可能受某些专用环境变量的影响。

2.2.2。生成的 JIT 函数

  1. @numba.generated_jit(nopython=False, nogil=False, cache=False, forceobj=False, locals={})

jit() 装饰器一样,但是在编译时调用装饰函数,传递函数参数的 类型 。修饰函数必须返回一个可调用函数,该函数将被编译为函数对这些类型的实现,允许灵活的专业化类型。

generated_jit() 装饰器返回 Dispatcher 对象。

2.2.3。调度程序对象

  1. class Dispatcher

通过调用 jit()generated_jit() 创建的对象类。您不应该尝试以任何其他方式创建此类对象。调用 Dispatcher 对象会为调用它的参数调用已编译的特化,使其充当已编译的 Python 函数的加速替换。

此外,Dispatcher 对象具有以下方法和属性:

  1. py_func

编译的纯 Python 函数。

  1. inspect_types(file=None, pretty=False)

打印出与相应的 Numba IR 一起逐行注释的函数源代码列表,以及各种变量的推断类型。如果指定了 文件 ,则对该文件对象进行打印,否则对 sys.stdout 进行打印。如果 漂亮 设置为 True,那么将在终端和笔记本中的 HTML 中生成彩色 ANSI。

也可以看看

Numba 架构

  1. inspect_llvm(signature=None)

返回将编译的函数签名键入到为函数生成的人类可读 LLVM IR 的字典。如果指定了签名关键字,则返回与该单个签名对应的字符串。

  1. inspect_asm(signature=None)

返回将编译的函数签名键入到该函数的人类可读的本机汇编程序代码的字典。如果指定了签名关键字,则返回与该单个签名对应的字符串。

  1. inspect_cfg(signature=None, show_wrapped)

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

可以对控制流图形对象进行字符串化(strrepr)以获得 DOT 格式的图形的文本表示。或者,使用其.display(filename=None, view=False)方法绘制图形。可以将 文件名 选项设置为要写入的呈现输出的特定路径。如果 视图 选项为 True,则图形格式(PDF)的系统默认应用程序将打开该图。在 IPython 笔记本中,返回的对象可以是内联图。

用法:

  1. @jit
  2. def foo():
  3. ...
  4. # opens the CFG in system default application
  5. foo.inspect_cfg(foo.signatures[0]).display(view=True)
  1. recompile()

重新编译所有现有签名。这可能很有用,例如,如果函数冻结了全局变量或闭包变量,并且 Python 中的值已更改。由于编译并不便宜,因此主要用于测试和交互式使用。

  1. parallel_diagnostics(signature=None, level=1)

打印给定签名的并行诊断信息。如果没有签名,则打印所有已知签名。 level用于调整详细程度,level=1(默认值)是最小详细程度,2,3 和 4 级提供更高级别的详细程度。

  1. get_metadata(signature=None)

获取给定签名的编译元数据。这对于 Numba 和 Numba 扩展的开发人员非常有用。

2.2.4。矢量化函数(ufuncs 和 DUFuncs)

  1. @numba.vectorize(*, signatures=[], identity=None, nopython=True, target='cpu', forceobj=False, cache=False, locals={})

编译装饰函数并将其包装为 Numpy ufunc 或 Numba DUFunc 。可选的 nopythonforceobjlocals 参数具有与 numba.jit() 相同的含义。

签名 是一个可选的签名列表,以与 numba.jit() 签名 参数相同的形式表示。如果 签名 非空,那么装饰器会将用户 Python 函数编译为 Numpy ufunc。如果没有给出 签名 ,那么装饰器会将用户 Python 函数包装在 DUFunc 实例中,只要 Numpy 找不到匹配项,它就会在调用时编译用户函数循环输入参数。如果 目标 "parallel",则需要 签名

标识 是正在实现的函数的标识(或单位)值。可能的值为 0,1,None 和字符串"reorderable"。默认值为 None。 None 和"reorderable"均表示该函数没有标识值; "reorderable"另外指定沿多个轴的减少可以重新排序。

如果有多个 签名 ,则必须从更具体到最不具体的顺序进行排序。否则,Numpy 基于类型的调度可能无法按预期工作。例如,以下是错误的:

  1. @vectorize(["float64(float64)", "float32(float32)"])
  2. def f(x): ...

因为在单精度数组上运行它将选择编译函数的float64版本,导致执行效率低得多。正确的调用是:

  1. @vectorize(["float32(float32)", "float64(float64)"])
  2. def f(x): ...

target 是后端目标的字符串;可用值为“cpu”,“parallel”和“cuda”。要使用多线程版本,请将目标更改为“parallel”(这需要指定签名):

  1. @vectorize(["float64(float64)", "float32(float32)"], target='parallel')
  2. def f(x): ...

对于 CUDA 目标,请使用“cuda”:

  1. @vectorize(["float64(float64)", "float32(float32)"], target='cuda')
  2. def f(x): ...

可以缓存已编译的函数以减少将来的编译时间。通过将 缓存 设置为 True 来启用它。只有“cpu”和“parallel”目标支持缓存。

  1. @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)"。它的定义可能是(函数体省略):

  1. @guvectorize(["void(float64[:,:], float64[:,:], float64[:,:])"],
  2. "(m,n),(n,p)->(m,p)")
  3. def f(a, b, result):
  4. """Fill-in *result* matrix such as result := a * b"""
  5. ...

如果其中一个参数应该是标量,则相应的布局规范是(),并且该参数将真正作为零维数组给出(您必须取消引用它才能获得标量值)。例如,具有可参数窗口宽度的一维移动平均值可以具有"(n),()->(n)"的布局串。

请注意,任何输出都将作为附加函数参数预先分配给您:您的代码必须使用您正在实现的函数的适当值填充它。

如果您的函数没有输出数组,则应省略布局字符串中的“箭头”(例如"(n),(n)")。

也可以看看

Numpy 支持的布局字符串的规范。请注意,Numpy 使用术语“签名”,我们不幸地将其用于其他内容。

可以缓存已编译的函数以减少将来的编译时间。通过将 缓存 设置为 True 来启用它。只有“cpu”和“parallel”目标支持缓存。

  1. class numba.DUFunc

通过调用没有签名的 numba.vectorize() 创建的对象类。

DUFunc 实例应该与 Numpy ufunc对象的行为类似,但有一个重要区别:调用时循环生成。当调用 ufunc 时,Numpy 会查看为该 ufunc 注册的现有循环,并且如果找不到一个无法安全地投射输入以适应的循环,则会引发TypeError。当呼叫 DUFunc 时,Numba 将呼叫委托给 Numpy。如果 Numpy ufunc 调用失败,那么 Numba 会尝试为给定的输入类型构建一个新循环,并再次调用 ufunc。如果第二次调用尝试失败或发生编译错误,则 DUFunc 将异常传递给调用者。

也可以看看

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

  1. ufunc

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

  1. nin

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

  1. nout

DUFunc 输出的数量。见 ufunc.nout

  1. nargs

可能的 DUFunc 参数总数(应为 nin + nout )。见 ufunc.nargs

  1. ntypes

DUFunc 支持的输入类型数。见 ufunc.ntypes

  1. types

以字符串形式提供的受支持类型的列表。见 ufunc.types

  1. identity

使用 ufunc 作为缩减时的标识值。见 ufunc.identity

  1. reduce(A, *, axis, dtype, out, keepdims)

通过沿一个轴应用 DUFunc,将 A 的尺寸减一。见 ufunc.reduce

  1. accumulate(A, *, axis, dtype, out)

累积将运算符应用于所有元素的结果。见 ufunc.accumulate

  1. reduceat(A, indices, *, axis, dtype, out)

在单个轴上使用指定切片执行(局部)缩减。见 ufunc.reduceat

  1. outer(A, B)

将 ufunc 应用于所有对( ab )与 aAbB 。见 ufunc.outer

  1. at(A, indices, *, B)

索引 指定的元素在操作数 A 上执行无缓冲就位操作。如果您使用的是 Numpy 1.7 或更早版本,则此方法将不存在。见 ufunc.at

注意

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

2.2.5。 C 回调

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

即时编译修饰函数以生成高效的机器代码。编译后的代码包含在一个瘦 C 回调中,使得它可以使用自然的 C ABI 进行调用。

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

nopython 缓存 是布尔标志。 locals 是局部变量名称到类型和签名的映射。它们都与 jit() 中的含义相同。

装饰器返回 CFunc 对象。

注意

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

  1. class CFunc

cfunc() 创建的对象类。 CFunc 对象公开以下属性和方法:

  1. address

已编译的 C 回调的地址,为整数。

  1. cffi

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

  1. ctypes

一个 ctypes 回调实例,好像它是使用 ctypes.CFUNCTYPE() 创建的。

  1. native_name

已编译的 C 回调的名称。

  1. inspect_llvm()

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