17 KiB
Calculet NPU 环境、源码与算子优化分析
日期:2026-08-01
源码版本:fd9bd632f346085920d523dd307a3e4c11cc0a05
Runtime:CalRT 0.7.6
镜像:calculet-llama:v0.4.8-release
1. 结论摘要
当前方案不是通用的逐算子 GGML NPU backend,而是“llama.cpp 负责服务、调度和采样,预编译 calbin 负责整段 Transformer 推理”的混合架构。
主路径如下:
- llama.cpp 完成 tokenizer、请求调度、slot 管理和采样。
- 根据
n_tokens > 1或n_tokens == 1选择 prefill/decode calbin 子模型。 - Runtime 设置
past_kv_cur_seq_len[0]和cur_seq_len[0]两个 CSR。 - 输入 token/position 复制到 Runtime buffer。
- 同步执行
calrt::infer()和OutputBuf::Wait()。 - logits 从 NPU 回传到 CPU,CPU 完成采样和下一 token 调度。
这套架构已经能稳定跑 Qwen3-30B-A3B,但当前 calbin 的硬件 batch 固定为 1,新模型接入依赖重新导出 ONNX、编译 prefill/decode 图和生成 calbin,不是仅在 llama.cpp 中新增几个 kernel 即可。
2. 硬件与系统环境
| 项目 | 当前值 | 影响 |
|---|---|---|
| PCIe 设备 | 20f5:c001 |
Calculet NPU |
| PCIe 链路 | Gen4 x4,16 GT/s x4 | 理论单向有效带宽约 7.9 GB/s,需重点控制 Host/Device 拷贝 |
| BAR | 64 MB + 64 MB + 4 MB | BAR 空间有限,主要数据应走 DMA/设备 DRAM |
| 驱动 | calculet_pci 1.0.0 |
DKMS 模块,已签名 |
| 设备节点 | /dev/calculet0 |
当前权限为全用户读写,建议收紧到专用组 |
| Host CPU | 32 逻辑核 | tokenizer、采样和 server 调度资源充足 |
| Host RAM | 约 96 GB | 模型文件和服务缓存空间充足 |
| NPU DRAM | 32768 MiB | 当前 Runtime 报告常驻使用 1024 MiB |
| NPU SRAM | 18 MiB,1000 MHz | 算子 tile 和中间缓存的关键约束 |
| Podman | 4.9.3 rootless | 短 SSH 会话会破坏 pause-process 状态 |
驱动可调参数:
poll_mode:中断与硬件轮询切换,可用于 decode 低延迟 A/B。dma_timeout_ms:长序列、大块参数或压力测试时的 DMA 超时控制。strict_validation:开发阶段建议开启,性能基准阶段再对比关闭开销。calculet_log_level:用于驱动级错误和 DMA 定位。max_boards:未来多卡场景的设备枚举上限。
3. 软件与构建状态
- 宿主机:Ubuntu 24.04,Linux 7.0.0-28-generic。
- 宿主编译器:GCC 13.3,CMake 3.28.3。
- 镜像编译器:GCC 11.4。
- llama.cpp:Release、
-O3 -DNDEBUG、shared libraries。 LLAMA_USE_CALRT=ON。CALRT_INSTALL_DIR=/usr/local/。- 实际链接
/usr/local/lib/libcalrt-linux-x86_64.so.0.7.6。 GGML_CALRT=OFF,因此ggml/src/ggml-calrt不是当前生产推理路径。
风险:源码中同时存在旧 hostrt backend、新 CalRT 整图路径和系统预编译 Runtime 三种集成痕迹。建议建立一个版本清单,明确 llama commit、CalRT SO、驱动、CalcoreRT、模型编译器和 calbin schema 的兼容矩阵。
4. 当前 calbin 能力
实测解析:
- calbin 版本:
v0.1。 max_batch_size=1。max_seq_len=40960。- 子模型数量:2。
- 子模型:prefill、decode。
- 芯片模式:双芯片
MCHIP。 - 参数分片:9.40 GB + 8.78 GB。
- KV cache:48 层、4 KV heads、head dimension 128、BF16 路径。
- 量化:主模型 W8A8/W4AF16 混合配置。
- Flash Attention:已编译进 calbin。
calbin 元数据问题:
- prefill 子模型的
modelType被标记为llm_decode。 - 两个子模型 workload 均为 0。
- 双芯片模型的
tarChipN为空。 - 这些字段会妨碍自动调度、模型自检和多卡拓扑选择,应由编译工具链修复。
5. 源码调用链
核心文件:
src/llama-calrt.cpp:生产 CalRT 整图执行路径。src/llama-kv-cache-calrt-adapter.cpp:把 llama memory API 适配到KvManager。tools/server/server.cpp:CalRT decode 接入 slot 调度和性能计时。src/models/qwen3moe.cpp:Qwen3-MoE 的标准 GGML 图定义,用于模型结构和 CPU/GGML 路径。ggml/src/ggml-calrt/*:未启用的实验 backend。
关键行为:
- 设备初始化固定使用 PCIe VirtualDevice。
- Runtime 初始化时直接读取两个固定寄存器获得 CalcoreRT commit。
n_tokens > 1选择 prefill,n_tokens == 1选择 decode。- prefill/decode 通过子模型名称子串查找,缺乏严格 schema 和唯一性校验。
- 每次
calrt_infer()创建新的 InputBuf 和 OutputBuf。 - 实际数据区使用长期
std::vector并通过MapBuf()复用。 - prefill 对输入和 logits 使用
SliceTensor()降低传输量。 - 每个 sequence 在 C++ 循环里依次执行同步 infer,当前没有真正的多 sequence 并行。
- KV 资源不足时使用
do/while(!ok)忙等,没有超时、退避或取消机制。 - 推理错误主要通过 exception、abort 和 GGML_ASSERT 处理,不适合生产服务降级。
6. 已暴露但未充分利用的性能数据
CalRT 已提供并且源码已采集:
GetWaitTime():纯 NPU infer 时间。GetInputTransferTime():H2D 时间。GetOutputTransferTime():D2H 时间。- framework unknown 时间:总同步时间减去以上三项。
- prefill 与 decode 分项累计。
建议新增稳定的 /calrt/metrics 或并入 Prometheus /metrics:
calrt_infer_ms{phase=prefill|decode}calrt_h2d_ms{phase=...}calrt_d2h_ms{phase=...}calrt_framework_ms{phase=...}calrt_kv_used_batchescalrt_kv_allocation_wait_mscalrt_device_dram_used_bytescalrt_device_statuscalrt_errors_total{code=...}
这是优先级最高的改造,因为没有分层时间就无法判断应该优化算子、DMA、Runtime 调度还是 server。
7. 当前性能与主要瓶颈
已测 Qwen3-30B-A3B:
- 27 token prefill:184.27 token/s。
- 246 token decode:58.25 token/s。
- 246 token 请求端到端:4.41 s。
瓶颈优先级判断:
P0:硬件 batch 固定为 1
max_batch_size=1 使 -np > 1 无法形成硬件批处理,只能排队或时间复用。要提升吞吐,必须重新编译支持 batch 4/8/16 的 decode calbin,并同步修改 KV layout 和 server batching。
P0:同步执行和逐 sequence 循环
每个 sequence 都执行 infer() 后立即 Wait()。应改为:
- 创建多个 in-flight OutputBuf。
- prefill/decode 分队列。
- 先提交可并行任务,再批量等待。
- 用事件/回调替代 host busy wait。
P0:KV 资源等待无超时
while(!Apply()) 会在资源耗尽或状态错误时永久占用 CPU。应增加超时、指数退避、请求取消和明确的 HTTP 429/503。
P1:每次推理创建 Buffer 对象
虽然底层数据内存复用,但 InputBuf/OutputBuf 对象、tensor lookup 和 hyperparameter lookup 仍重复发生。应按 phase 和 slot 建立 buffer pool,并缓存 tensor/CSR handle。
P1:logits D2H 与 CPU sampling
每个 decode token 都回传完整 vocab logits。Qwen3 vocab 为 151936,BF16 全量约 297 KiB/token;58 token/s 时仅 logits 就约 17 MiB/s,带宽不是 Gen4 x4 的上限,但延迟、DMA 启动和 CPU sampling 会形成串行链。
可选优化:
- NPU 侧 top-k,返回 token id、score 和少量候选。
- 只在 logprobs 请求时回传更多 logits。
- NPU 侧 fused lm_head + top-k + sampling。
- 双缓冲 logits,使下一次 decode 与上一次 sampling 重叠。
P1:prefill 输出切片存在硬编码 tile
代码硬编码 D0_tmp=16,并依据 output rank 选择多套 untile 函数。应把 layout、tile 和有效输出范围放入 calbin tensor metadata,避免新模型/新编译器版本依赖 C++ 分支。
P2:PCIe 和驱动等待策略
Gen4 x4 足以承载当前 token/position/logits,但小 DMA 的固定启动成本可能影响 decode。需要用 Runtime 分项计时做以下 A/B:
- interrupt vs
poll_mode=1 - pageable host vector vs pinned/registered host memory
- 单缓冲 vs 双缓冲
- 每 token DMA vs 批量/候选压缩
8. Qwen3-MoE 算子优化方向
Qwen3-30B-A3B 每层主要结构:
- RMSNorm
- Q/K/V projection
- Q/K RMSNorm
- RoPE
- GQA attention + KV cache
- output projection + residual
- RMSNorm
- MoE router softmax/top-k
- 8 个激活 expert 的 gate/up/down projection
- SiLU 门控和 residual
建议优先优化:
8.1 MoE dispatch 与 expert GEMM
- 将 router softmax、top-k、token permutation 和 expert offset 生成融合。
- 对 decode batch=1 使用专用 sparse expert path,避免为 128 experts 建立大 dispatch 表。
- 将 8 个激活 expert 的小 GEMM 合并成 grouped GEMM。
- 按 expert 热度调整权重布局,减少跨芯片访问。
- 对 prefill 使用 token-expert 分桶和负载均衡;对 decode 使用固定低延迟路径。
- 评估 expert parallel:两芯片各放一组 experts,attention 权重复制或按层切分。
8.2 Attention/KV
- 保留 fused Flash Attention。
- decode 使用 GQA 专用 kernel,4 KV heads 不应展开成 32 heads。
- KV cache layout 应让 batch、head 和 sequence tile 与 DMA/compute tile 一致。
- 支持 S8 V-cache 是 Runtime 头文件已有能力,可作为长上下文容量特性开放。
- KV shift/rope 更新应异步执行,并与下一层或下一请求重叠。
8.3 Norm/Residual/RoPE 融合
- RMSNorm + QKV projection 融合。
- attention output projection + residual 融合。
- FFN output + residual 融合。
- Q/K norm + RoPE 融合。
- 对 decode 的单 token 路径生成独立 kernel,避免复用 prefill 的通用调度。
8.4 量化
- 当前 W8A8/W4AF16 应按层做敏感度分析,而不是全局固定。
- attention projection、router 和 lm_head 通常比 expert FFN 更敏感。
- 优先尝试 expert W4、attention W8、activation BF16/动态 INT8。
- 建立 perplexity、关键任务准确率和 token/s 的三维回归门槛。
9. 新模型算子优化与接入流程
阶段 A:模型结构归一化
- 确认 tokenizer、chat template 和特殊 token。
- 固化模型 hparams:layers、heads、KV heads、head dim、rope、experts、top-k、activation。
- 将 Hugging Face 图导出为 prefill 与 decode 两张 ONNX。
- 明确动态维:prompt length、past KV length、batch。
- 保持稳定 I/O schema:
inputs[0]、inputs[1]、outputs[0]只是当前约定,建议改为语义名称和版本化 manifest。
阶段 B:算子覆盖分析
生成 op inventory,分为:
- NPU 原生支持。
- 可由现有 kernel 组合。
- 值得融合的新 kernel。
- 暂时回退 CPU 的低占比 op。
- 阻塞模型接入的 unsupported op。
对每个 op 记录 dtype、shape 范围、layout、动态轴、精度容差和预期占比。
阶段 C:kernel 开发
每个新 kernel 至少包含:
- correctness golden test。
- 边界 shape 和动态 shape。
- BF16/INT8/INT4 精度比较。
- 单芯片和双芯片布局。
- 性能基线:吞吐、延迟、SRAM、DRAM、DMA。
- 与融合前图的端到端收益。
阶段 D:calbin 编译
- 分别编译 prefill 和 decode。
- 生成 batch 1 低延迟 decode。
- 生成 batch 4/8/16 吞吐 decode。
- 为常用 context bucket 生成 prefill 变体,或验证真正动态 shape。
- 把 tile、layout、I/O、KV 和芯片映射写入版本化 metadata。
- 生成 golden input/output,并在 Runtime
GetGolden*接口上做自动验证。
阶段 E:llama/server 适配
- 不再用子串选择模型,改用明确的 role/type manifest。
- 按 batch、phase、context bucket 选择 calbin 子模型。
- 增加 buffer pool 和 async queue。
- 实现失败回滚、超时和可观测错误码。
- 对不兼容特性在启动时 fail-fast,而不是运行中 abort。
阶段 F:验证
- 单 op golden。
- 子图 golden。
- prefill/decode logits 对齐。
- 长上下文 KV 正确性。
- 多 slot 并发。
- soak test、设备 reset 后恢复、PCIe 错误注入。
- 性能回归:TTFT、TPOT、tokens/s、P50/P95/P99。
10. 可开放特性矩阵
可以较快开放
| 特性 | 当前基础 | 所需工作 |
|---|---|---|
| Prometheus CalRT 分项指标 | 时间已采集 | 接入 /metrics |
| 设备状态/内存只读 API | Runtime 已提供 | 鉴权、单位修复、限频 |
| tokenizer/detokenizer | server 已有端点 | 接口测试与文档化 |
| slots/health/models/props | server 已有端点 | 增加 CalRT 字段 |
| 流式与非流式 completion | 已验证 | 补协议回归测试 |
| 固定 40960 长上下文 | calbin 支持 | 长上下文稳定性与 KV 压测 |
| prefix/prompt cache | server 已有 | 验证 CalRT KV adapter 一致性 |
| 性能 debug dump | Runtime 有 debug/replay | 增加安全路径、空间配额和自动清理 |
需要 Runtime/server 改造后开放
| 特性 | 当前阻塞 |
|---|---|
| 真正多并发 | calbin max batch=1、同步 infer |
| continuous batching | server 有逻辑,硬件侧没有 batch calbin |
| dynamic batching | prefill/decode 不能混合,模型选择只看 token 数 |
| speculative decoding | 需要 draft model calbin、双模型 KV 协同和调度 |
| LoRA/aLoRA | 当前整图权重固化在 calbin,标准 llama LoRA 不会自动作用于 NPU 图 |
| embeddings/rerank | 需要对应 encoder/ranker calbin 和 pooling 输出 |
| multimodal | 源码有 vision 子模型入口,但当前 calbin 只有 prefill/decode |
| tool-use/json grammar | CPU sampling 层可用,但需验证 reasoning/chat template 行为 |
| KV quantization | Runtime 有 S8 V-cache 枚举,需模型编译和精度验证 |
| multi-card scaling | 元数据芯片列表为空,缺设备拓扑和调度策略 |
| NPU sampling | 当前完整 logits 回 CPU,需要新增 top-k/sampling kernel 和 API |
不应直接开放
- 未鉴权的设备 reset、清配置和寄存器读写。
- 任意设备地址的内存读写。
- 用户可控 debug dump 路径。
- 未限流的 power mode 与设备管理接口。
这些接口应只放在受控运维面,并提供 allowlist、审计日志和互斥锁。
11. 建议的实施顺序
第一阶段:可观测与稳定性
- 暴露 infer/H2D/D2H/framework/KV 指标。
- 修复
GetMemoryUsage()total 值异常。 - 修复 KV busy wait,加入超时和 429/503。
- 统一版本与 calbin metadata schema。
- 修复 Podman 持久会话,建议为 user2 开启 linger 或使用 systemd user service。
第二阶段:低风险性能收益
- Buffer object pool 和 tensor/CSR handle cache。
- pinned host memory 与双缓冲。
- decode interrupt/polling A/B。
- logits top-k 压缩。
- prefill context bucket 基准。
第三阶段:吞吐架构
- 编译 batch 4/8/16 decode calbin。
- prefill/decode 分队列和异步 submit/wait。
- 多 slot KV 资源管理和背压。
- continuous batching 端到端验证。
第四阶段:新模型平台化
- 自动 op inventory 和 unsupported report。
- 版本化 model manifest。
- 自动 calbin golden test。
- kernel microbenchmark 与精度回归平台。
- 以一个 dense 模型和一个 MoE 模型验证通用流程。
12. 下一轮测试矩阵
- prompt 长度:1、16、128、512、2048、8192、32768、40960。
- output 长度:1、32、128、512。
- slot:1、2、4;当前用于验证排队行为,不宣称硬件并发。
- 指标:TTFT、TPOT、总吞吐、infer、H2D、D2H、framework、CPU、NPU memory。
- 驱动模式:interrupt 与 polling。
- 稳定性:30 分钟、2 小时、24 小时。
- KV:创建/删除/复用/shift、上下文接近 40960、取消请求。
- API:completion/chat/stream/tokenize/detokenize/slots/metrics/props。
- 错误:非法 calbin、超长 context、资源耗尽、客户端断连、容器重启。
13. 当前明确问题清单
GetMemoryUsage().second返回约3.05e-05 MiB,与 32768 MiB 冲突。- calbin prefill model type 错标为 decode。
- calbin workload 为 0、目标芯片列表为空。
chat_template_kwargs.enable_thinking=false未生效。- 短
max_tokens会全部消耗在 reasoning,最终 content 为空。 - rootless Podman pause process 随短 SSH session 消失。
- KV allocation 使用无限忙等。
- Runtime 错误路径存在 abort/assert,缺服务级恢复。
- 子模型选择使用字符串包含匹配,存在歧义。
- 未启用的
ggml-calrtbackend 报告固定 16 KiB memory,支持/执行 op 列表还不一致,不能作为生产能力声明。 /dev/calculet0权限过宽。- user2 实际属于 sudo 组,与接入文档描述不一致。
14. 保持状态
- VPN 已重新连接并保持运行。
- SSH 可用。
- 远端保留聚焦源码归档,便于继续分析。
- 本地保留聚焦源码、测试程序和分析材料。
- 未再次启动模型服务,避免无测试时占用 NPU。