与Python语义的偏差
边界检查
默认情况下,在Numba编译的函数中访问数组的越界索引不会引发 IndexError
,而是返回无效值或导致访问冲突错误(它从无效的内存位置读取)。可以通过jit装饰器的 boundscheck 选项在特定函数上启用边界检查。此外,可以将 NUMBA_BOUNDSCHECK
设置为0或1以全局覆盖此标志。
备注
边界检查会减慢典型函数的运行速度,因此建议仅在调试目的时使用此标志。
异常和内存分配
由于当前编译器在处理异常时的限制,在引发异常的函数内分配的内存(几乎总是 NumPy 数组)将会 泄漏。这是一个已知问题,将会得到修复,但在此期间,最好在不会引发异常的函数外部进行内存分配。
整数宽度
虽然 Python 有任意大小的整数,但在 Numba 编译的函数中,整数通过 类型推断 获得固定大小(通常是机器整数的大小)。这意味着算术运算可能会回绕、产生未定义结果或溢出。
如果需要对整数宽度进行精细控制,可以通过显式类型指定来覆盖类型推断。
布尔取反
在Python布尔值上调用按位补码运算符(~
运算符)会返回一个整数,而在Numpy布尔值上调用相同的运算符会返回另一个布尔值:
>>> ~True
-2
>>> ~np.bool_(True)
False
Numba 遵循 Numpy 的语义。
全局变量和闭包变量
在 nopython 模式 中,Numba 冻结了全局变量和闭包变量:Numba 编译的函数在编译时看到这些变量的值。此外,无法从函数中更改这些变量的值。
Numba 可能或可能不 复制编译函数内部引用的全局变量。小的全局数组会被复制,以便在假设不可变性的情况下进行潜在的编译器优化。然而,大的全局数组不会被复制以节省内存。”小”和”大”的定义可能会有所变化。
变量的零初始化
Numba 在运行时不跟踪变量的生命周期。为了实现简单,所有变量都被初始化为零。例如:
from numba import njit
@njit
def foo():
for i in range(0):
pass
print(i) # will print 0 and not raise UnboundLocalError
foo()