numba库 核心算法加速
用于核心算法加速,加速数值计算。比如numpy库等加速都可以
官方文档:Numba documentation — Numba 0+untagged.1510.g1e70d8c.dirty documentation
使用方法
一、直接使用
问题:Numba 会在第一次调用函数时,根据输入参数的类型动态生成优化后的机器码,这一过程需要时间。但是编译后的机器码会缓存到磁盘(.numba_cache 目录),后续调用直接使用缓存,因此速度极快
解决方案:
- 方案1、预热调用:可以 构造一个符合的参数,在软件启动时先主动调用一次
- 方案2、AOT编译:将关键函数 提前编译为二进制文件,直接跳过运行时的 JIT 过程
- 方案3、缓存优化:利用 Numba 的 磁盘缓存机制,确保编译结果持久化。
from numba import njit @njit(cache=True) # 确保缓存生效 def your_function(...): ...
二、AOT编译
效率最高,适合生产环境:尤其适用于需要部署的应用程序。
AOT编译代码:
from numba.pycc import CC
import numpy as np
cc = CC('precompiled')
cc.verbose = True # 开启调试信息
# ------------------------- 函数定义 -------------------------
def triangleInsideSphere(point, radiusSq, v1, v2, v3):
def distanceSqToSegment(point, v1, v2):
v2v1 = v2 - v1
len_sq = v2v1 @ v2v1
if np.abs(len_sq) < 1e-20:
delta = point - v1
return delta @ delta
pt = point - v1
t = (pt @ v2v1) / len_sq
if t < 0.0:
return pt @ pt
elif t > 1.0:
delta = point - v2
return delta @ delta
else:
projection = pt - t * v2v1
return projection @ projection
d1 = distanceSqToSegment(point, v1, v2)
d2 = distanceSqToSegment(point, v2, v3)
d3 = distanceSqToSegment(point, v1, v3)
return d1 < radiusSq or d2 < radiusSq or d3 < radiusSq
def pointInsideTriangle(point, v1, v2, v3):
eps = 1e-10
A, B, C = v1, v2, v3
P = point
vec0 = C - A
vec1 = B - A # 修正变量名冲突
vec2 = P - A
dot00 = vec0 @ vec0
dot01 = vec0 @ vec1
dot02 = vec0 @ vec2
dot11 = vec1 @ vec1
dot12 = vec1 @ vec2
denom = dot00 * dot11 - dot01 ** 2
if np.abs(denom) < eps:
return False
inv_denom = 1.0 / denom
u = (dot11 * dot02 - dot01 * dot12) * inv_denom
v_val = (dot00 * dot12 - dot01 * dot02) * inv_denom # 修正变量名
return (u >= -eps) & (v_val >= -eps) & (u + v_val <= 1.0 + eps)
# ------------------------- 显式类型绑定 -------------------------
sig_triangle = 'b1(f4[::1], f4, f4[::1], f4[::1], f4[::1])'
sig_point = 'b1(f4[::1], f4[::1], f4[::1], f4[::1])'
cc.export('triangleInsideSphere', sig_triangle)(triangleInsideSphere)
cc.export('pointInsideTriangle', sig_point)(pointInsideTriangle)
if __name__ == '__main__':
cc.compile()
运行上述代码之后,会得到一个precompiled.pyd文件(名字取决于上面CC函数定义的名字)
调用方式:直接使用下面的代码引入函数即可
from precompiled import triangleInsideSphere, pointInsideTriangle
显式类型绑定中,符号意义:
| 类型 | 对应类型 | 含义 |
|---|---|---|
| b1 | np.bool_ | 布尔返回值 |
| i4 | np.int32 | 32位整数 |
| i8 | np.int64 | 64位整数 |
| f4 | np.float32 | 32位单浮点数 |
| f8 | np.float64 | 64位双精度浮点数 |
| u1 | np.uint8 | 8位无符号整数 |
注意事项:
- 显式类型绑定中,类型必须是正确的,尤其是numpy对象,建议显式定义dtype类型
- 预编译的功能,正在等待被弃用。
解决 PyCharm 中未解析引用警告的完整方案
PyCharm 对 Numba AOT 生成的 .pyd 文件中的函数无法正确识别,导致静态检查误报。以下是三种解决方案,按推荐度排序:
方案 1:生成存根文件(.pyi)(推荐)
通过生成类型存根文件,明确告知 PyCharm 函数的参数和返回类型。
步骤 1:安装 mypy 工具
pip install mypy
步骤 2:生成存根文件
在项目根目录执行:
stubgen -m precompiled -o .
生成 precompiled.pyi 文件。
步骤 3:编辑存根文件
打开 precompiled.pyi,根据实际函数签名修改内容:
# precompiled.pyi
from numba import float32, boolean
def triangleInsideSphere(
point: float32[::1],
radiusSq: float32,
v1: float32[::1],
v2: float32[::1],
v3: float32[::1]
) -> boolean: ...
def pointInsideTriangle(
point: float32[::1],
v1: float32[::1],
v2: float32[::1],
v3: float32[::1]
) -> boolean: ...
步骤 4:配置 PyCharm
• 将存根文件放在与 .pyd 同目录或项目根目录。
• 右键项目根目录 → Mark Directory as → Sources Root。
方案 2:动态类型注释(临时解决)
在导入语句后添加类型注释,直接告知 PyCharm 函数存在。
# noinspection PyUnresolvedReferences
from precompiled import triangleInsideSphere, pointInsideTriangle
# type: (np.ndarray, float, np.ndarray, np.ndarray, np.ndarray) -> bool
# type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray) -> bool
方案 3:配置 PyCharm 忽略警告
全局关闭未解析引用检查(不推荐,会丢失其他有效警告)。
步骤
- File → Settings → Editor → Inspections。
- 搜索 Unresolved references → 取消勾选。
验证与注意事项
- 存根文件更新:每次修改 AOT 编译的函数后,需重新生成并更新
.pyi。 - 类型一致性:确保存根中的类型签名与 Numba 导出完全一致(如
float32vsnp.float32)。 - 运行时无影响:存根文件仅用于静态检查,不影响实际执行。
最终效果
• 方案 1 可永久消除警告,提升代码可维护性。
• 方案 2 适合快速验证,但需每次手动添加。
• 方案 3 会隐藏所有未解析引用错误,慎用。