1.16。常见问题
1.16.1。编程
1.16.1.1。我可以将函数作为参数传递给 jitted 函数吗?
从 Numba 0.39 开始,你可以,只要函数参数也被 JIT 编译:
@jit(nopython=True)
def f(g, x):
return g(x) + g(-x)
result = f(jitted_g_function, 1)
但是,使用作为函数的参数进行调度会产生额外的开销。如果这对您的应用程序很重要,您还可以使用工厂函数来捕获闭包中的函数参数:
def make_f(g):
# Note: a new f() is created each time make_f() is called!
@jit(nopython=True)
def f(x):
return g(x) + g(-x)
return f
f = make_f(jitted_g_function)
result = f(1)
提高 Numba 功能的调度性能是一项持续的任务。
1.16.1.2。当我修改全局变量 时,Numba 似乎并不关心
Numba 将全局变量视为编译时常量。如果您希望在修改全局变量值时自己的 jitted 函数自行更新,一种解决方案是使用 recompile()
方法重新编译它。但这是一个相对较慢的操作,因此您可以决定重新构建代码并将全局变量转换为函数参数。
1.16.1.3。我可以调试 jitted 功能吗?
Numba 编译的代码目前不支持调用 pdb
或其他此类高级设施。但是,您可以通过设置 NUMBA_DISABLE_JIT
环境变量来暂时禁用编译。
1.16.1.4。如何创建 Fortran 排序的数组?
Numba 目前不支持大多数 Numpy 函数的order
参数,例如 numpy.empty()
(由于类型推断算法的限制)。您可以通过创建 C 有序数组然后转置它来解决此问题。例如:
a = np.empty((3, 5), order='F')
b = np.zeros(some_shape, order='F')
可以改写为:
a = np.empty((5, 3)).T
b = np.zeros(some_shape[::-1]).T
1.16.1.5。如何增加整数宽度?
默认情况下,Numba 通常使用整数变量的机器整数宽度。在 32 位机器上,您有时可能需要 64 位整数的大小。您可以简单地将相关变量初始化为np.int64
(例如np.int64(0)
而不是0
)。它将传播到涉及这些变量的所有计算。
1.16.1.6。如何判断parallel=True
是否有效?
将环境变量 NUMBA_WARNINGS
设置为非零,如果parallel=True
转换因装饰的功能失败,将显示警告。
此外,设置环境变量 NUMBA_DEBUG_ARRAY_OPT_STATS
将显示有关哪些运算符/调用转换为并行 for 循环的一些统计信息。
1.16.2。表现
1.16.2.1。 Numba 内联功能吗?
Numba 为 LLVM 提供了足够的信息,因此可以内联足够短的函数。这仅适用于 nopython 模式。
1.16.2.2。 Numba 矢量化阵列计算(SIMD)吗?
Numba 本身并没有实现这样的优化,但它允许 LLVM 应用它们。
1.16.2.3。为什么我的循环没有矢量化?
Numba 默认启用 LLVM 中的循环向量化优化。虽然它是一个强大的优化,但并非所有循环都适用。有时,循环向量化可能会因内存访问模式等细微细节而失败。要从 LLVM 查看其他诊断信息,请添加以下行:
import llvmlite.binding as llvm
llvm.set_option('', '--debug-only=loop-vectorize')
这告诉 LLVM 从 loop-vectorize 传递到 stderr 打印调试信息。每个函数条目如下所示:
LV: Checking a loop in "<low-level symbol name>" from <function name>
LV: Loop hints: force=? width=0 unroll=0
...
LV: Vectorization is possible but not beneficial.
LV: Interleaving is not beneficial.
每个函数条目由空行分隔。拒绝矢量化的原因通常是在条目结束时。在上面的示例中,LLVM 拒绝了矢量化,因为这样做不会加速循环。在这种情况下,它可能是由于内存访问模式。例如,循环的数组可能不是连续的布局。
当内存访问模式不重要,无法确定访问内存区域时,LLVM 可能会拒绝以下消息:
LV: Can't vectorize due to memory conflicts
另一个常见原因是:
LV: Not vectorizing: loop did not meet vectorization requirements.
在这种情况下,矢量化被拒绝,因为矢量化代码可能表现不同。这是尝试打开fastmath=True
以允许 fastmath 指令的情况。
1.16.2.4。 Numba 会自动并行化代码吗?
在某些情况下,它可以:
- 具有
target="parallel"
选项的 Ufuncs 和 gufunc 将在多个线程上运行。 @jit
的parallel=True
选项将尝试优化阵列操作并并行运行。它还增加了对prange()
的支持,以显式并行化循环。
您也可以自己在多个线程上手动运行计算并使用nogil=True
选项(参见释放 GIL )。 Numba 还可以使用其 CUDA 和 HSA 后端在 GPU 架构上实现并行执行。
1.16.2.5。 Numba 可以加快短期运行功能吗?
不显着。新用户有时希望 JIT 编译这样的函数:
def f(x, y):
return x + y
并获得 Python 解释器的显着加速。但是 Numba 没有太多可以改进的地方:大部分时间都可能花在 CPython 的函数调用机制上,而不是函数本身。根据经验,如果函数执行时间不到 10μs:请保留它。
例外情况是你应该 JIT 编译该函数,如果它是从另一个 jitted 函数调用的。
1.16.2.6。当 JIT 编译一个复杂的函数时有一个延迟,我该如何改进呢?
尝试将cache=True
传递给@jit
装饰器。它会将编译后的版本保留在磁盘上供以后使用。
更激进的替代方案是提前编译。
1.16.3。 GPU 编程
1.16.3.1。如何解决CUDA intialized before forking
错误?
在 Linux 上,Python 标准库中的multiprocessing
模块默认使用fork
方法创建新进程。由于在父进程和子进程之间分配重复状态的进程,如果 CUDA 运行时在之前被初始化到 fork,CUDA 将无法在子进程中正常工作。 Numba 检测到这一点并使用CUDA initialized before forking
消息引发CudaDriverError
。
避免此错误的一种方法是在子进程内或创建进程池后对numba.cuda
函数进行所有调用。但是,这并不总是可行,因为您可能希望在启动进程池之前查询可用 GPU 的数量。在 Python 3 中,您可以更改进程启动方法,如多处理文档中所述。从fork
切换到spawn
或forkserver
将避免 CUDA 初始化问题,尽管子进程不会从其父进程继承任何全局变量。
1.16.4。与其他工具集成
1.16.4.1。我可以“冻结”使用 Numba 的应用程序吗?
如果您使用 PyInstaller 或类似的实用程序来冻结应用程序,则可能会遇到 llvmlite 的问题。 llvmlite 需要一个非 Python DLL 才能正常工作,但冻结实用程序不会自动检测到它。您必须通知冻结实用程序 DLL 的位置:它通常会被命名为llvmlite/binding/libllvmlite.so
或llvmlite/binding/llvmlite.dll
,具体取决于您的系统。
1.16.4.2。在 Spyder 下运行两次脚本时出错
当您在 Spyder 下的控制台中运行脚本时,Spyder 会首先尝试重新加载现有模块。这对 Numba 不起作用,并且可能产生TypeError: No matching definition for argument type(s)
之类的错误。
Spyder 首选项中有一个修复程序。打开“首选项”窗口,选择“控制台”,然后选择“高级设置”,单击“设置 UMR 排除模块”按钮,并在弹出的文本框内添加numba
。
要查看设置是否生效,请确保重新启动 IPython 控制台或内核。
1.16.4.3。为什么 Numba 抱怨当前的语言环境?
如果收到如下错误消息:
RuntimeError: Failed at nopython (nopython mode backend)
LLVM will produce incorrect floating-point code in the current locale
这意味着您遇到了 LLVM 错误,导致浮点常量处理不正确。已知某些第三方库(如 Qt 后端到 matplotlib)会发生这种情况。
要解决此问题,您需要将语言环境强制恢复为其默认值,例如:
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
1.16.5。杂项
1.16.5.1。如何在其他工作中引用/引用/确认 Numba?
对于学术用途,最好的选择是引用我们的 ACM 程序: Numba:基于 LLVM 的 Python JIT 编译器。