npunlock 在 Intel Core Ultra NPU 上启用自定义 C 内核
TL;DR
npunlock 提供了一种工作流程,可将任意 C 代码编译为 Intel Core Ultra NPU3720 内部 SHAVE 核心的机器码,并在标准 OpenVINO 风格的计算图中执行这些内核,从而将 NPU 扩展到 Intel 官方支持的操作集之外。
npunlock 的功能
npunlock 重建了从用户编写的 C 代码到可运行 NPU 内核的缺失路径。它:
- 使用 Intel/Movidius MoviTools 将 C 源码编译为 ACT-SHAVE 机器码。
- 将编译后的内核打包为自定义操作,可插入到 OpenVINO 兼容的计算图中。
- 使用 Intel 现有的驱动和编译器处理周围的计算图,因此只有自定义节点由 npunlock 处理。
- 生成 OpenVINO 格式的 IR,使管线的其余部分保持不变。
“Intel 在其 NPU 中提供了可编程的 SHAVE 核心,但公共软件栈仅暴露了图级编程。
npunlock重建了从自定义 C 代码到可运行 NPU 内核的缺失路径。” – npunlock README
为什么这很重要
Intel 的 NPU 软件只接受由专有编译器已知操作组成的计算图。没有公共 API 可以为新操作提供手写 C 实现,这实际上将开发者锁在了 SHAVE 核心之外。npunlock 打开了这把锁,支持:
- 对 Intel 尚未(或永远不会)支持的新算子进行研究。
- 通过编写手写优化内核进行细粒度性能调优。
- 探索混合精度管线,在单个计算图中结合 FP32 一元和 FP16 二元自定义分支(2026-09-23 宣布的突破)。
快速入门示例(FP32 GELU)
以下 Python 片段演示了完整的端到端流程:
import numpy as np, npunlock as npu
npu.configure(movi_dll_dir=r"C:\path\to\MVC_DEPEND")
gelu_c = b"""
#define MLIBM_DEFINE_LINK_COMPAT 1
#include <npunlock/npu3720_kernel.h>
void controlled_act(unsigned layerParams) {
act_abi_invocation invocation;
ACT_ABI_LOAD_INVOCATION32_OR_RETURN(layerParams, invocation);
const float *in = ACT_ABI_INPUT_PTR32(const float, invocation, 0u);
float *out = ACT_ABI_OUTPUT_PTR32(float, invocation, 1u);
const float SQRT_2_DIV_PI = 0.7978845608028654f;
for (unsigned i = 0; i < invocation.element_count; ++i) {
float x = in[i];
float w = x + 0.044715f * x * x * x;
w = tanhf(w * SQRT_2_DIV_PI);
out[i] = 0.5f * x * (1.0f + w);
}
}
"""
N = 2048
x = npu.input("x", shape=(1, N), dtype="f32")
y = npu.custom(x, source=gelu_c, carrier="Abs", _name="y")
program = npu.compile(npu.Graph(inputs=[x], outputs=[y], name="gelu_f32_example"))
input_value = np.linspace(-4, 4, N, dtype=np.float32).reshape(1, -1)
output = program.run({"x": input_value})["y"]
reference = 0.5 * input_value * (1.0 + np.tanh(np.sqrt(2.0/np.pi) * (input_value + 0.044715 * input_value**3)))
print(f"maximum absolute error: {np.max(np.abs(output - reference)):g}")
在配备 Meteor Lake CPU 和 NPU3720 的 Windows x64 机器上运行该脚本,报告的最大绝对误差极小,确认了功能正确性。
支持的功能(截至最新版本)
- 自定义内核编译:通过 MoviTools 将 C 编译为 ACT-SHAVE 机器码。
- 计算图集成:自定义节点可与 Intel 提供的操作共存。
- 数据类型:静态密集 FP16 一元/二元内核和已验证的 FP32 一元路径。
- 混合精度计算图:单个计算图可包含独立的 FP32 一元和 FP16 二元自定义分支。
- 数学库:通过捆绑的
mlibm.a符号清单提供非线性函数(如tanhf)。 - API:提供 Python、CLI 和原生 C 接口。
当前限制
- 平台:仅支持 Windows x64;Linux 支持未经测试。
- 硬件:仅在 Meteor Lake / Intel NPU3720 上验证。其他代际尚未验证。
- 静态形状:仅支持静态张量形状;动态形状尚未处理。
- ACT 载体:仅兼容的 ACT 载体(如
Abs)可承载自定义内核。 - 混合精度转换组:不可自动发现;混合精度示例使用独立分支。
“支持是实验性的,目前仅限于 Windows x64、Meteor Lake / NPU3720、静态形状、兼容的 ACT 载体和已知的张量布局。” – npunlock README
开始使用
先决条件
- 硬件:配备 Meteor Lake CPU 和 Intel NPU3720 的 Windows x64 机器。
- 驱动程序:安装设备的官方 Intel NPU 驱动程序。
- 工具链:Python 3.10+、CMake 3.24+、MSVC 工具链,以及 MoviTools
MVC_DEPEND包(从旧版 Lenovo 驱动包中提取,不要安装驱动本身)。
安装步骤
# 克隆仓库并安装 Python 包
git clone https://github.com/hsfzxjy/npunlock.git
cd npunlock
python -m pip install .
该包捆绑了 npunlock.dll 和 npunlock_worker.exe,因此除了指向 MVC_DEPEND 外,无需额外的原生路径配置。
运行 GELU 示例
$env:NPUNLOCK_MOVITOOLS_DIR = 'C:\path\to\MVC_DEPEND'
python examples\example_gelu_f32.py
该脚本编译自定义内核,将其插入计算图,在 NPU 上执行,并打印与 NumPy 相比的最大绝对误差。
Hacker News 上的社区反馈
- @ur-whale 指出仅限 Windows 的要求是一个障碍。
- @Gigachad 询问了实际用例;项目作者回应称,它支持 Intel 官方不支持的推理任务的“裸机 NPU 编程”。
- @Bayard_ne 强调了对自定义、非传统推理工作负载直接访问的兴奋。
- @alex7o 建议将该方法扩展到 Qualcomm Hexagon,表明对更广泛适用性的兴趣。
这些评论既凸显了对底层 NPU 黑客行为的热情,也体现了对跨平台支持的渴望。
为 Linux 支持和更新 NPU 做贡献
该仓库欢迎以下方面的贡献:
- Linux 移植 – 测试 Windows 生成的 SHAVE 镜像是否能在 Linux 上原样运行,并构建 Linux 兼容的 MoviTools 包装器。
- 新硬件 – 检查现有的
3720xxSHAVE 镜像是否能在更新的 Intel NPU 上工作,或 OEM 驱动包是否提供匹配的工具链。
这两项工作都需要硬件验证、驱动/固件版本跟踪,以及与主机 oracle 的数值比较。详细指南可在 Porting to Linux and newer NPUs wiki 页面中找到。
文档概览
- 获取 MoviTools – 如何在不安装旧驱动的情况下获取编译器。
- Python API – 构建、编译和执行计算图。
- 编写自定义内核 – 入口点约定、张量处理和示例内核。
- npunlock 的工作原理 – 计算图编译和内核注入的内部机制。
- 逆向工程突破 – 使自定义内核成为可能的实验。
- 当前限制 – 完整的兼容性矩阵。
- 开发和原生 API – 构建系统、测试和 C 接口。
所有文档都位于仓库的 wiki 文件夹中,并从 README 链接。
许可证
npunlock 根据 Apache License 2.0 发布。专有依赖项(如 MoviTools 和 Intel/Movidius 库)不重新分发,并保留其原始许可证。
Sources
相关
- 项目
- Dispatch
- 项目
- 项目
- 项目