Files
calculet-npu-research-archive/reports/Calculet-NPU-新模型适配与算子优化教程-20260802.md
T

18 KiB
Raw Blame History

Calculet NPU 新模型适配与算子优化教程

日期:2026-08-02
适用基线:CalRT 0.7.6、calculet-llama:v0.4.8-release、llama.cpp fd9bd632
参考模型:Qwen3-30B-A3B,双芯粒,40960 contextbatch 1

实施下钻:新模型的逐 Gate 输入/产物/命令/回退见《Calculet NPU 新模型适配工程 Runbook》;GEMM、Flash Attention、KV、MoE、D2D、fusion 与 sampling 的逐项 A/B 见《Calculet NPU 逐算子优化实验手册》。本文作为方法总览,不替代两份实施规范。

1. 本教程能做什么

当前资料足以完整定义新模型适配工程、准备模型和测试资产、修改服务适配层、分析现有 calbin,并与厂商共同验收新产物;但不足以在本地独立生成 NPU calbin。缺失项包括 calcc、ONNX passes、量化工具、kernel/codegen、C oplib、打包器及其版本化容器。

本文对每个阶段使用统一状态:

状态 含义 本教程中的处理方式
C0 当前产品或产物已经确认 可以直接复现、回归或改进
C1 SDK 0.7.6 存在,生产未使用 先做隔离实验,不能直接宣称产品支持
C2 用现有源码和公开接口可工程实现 我方可以开发,但要经过回归和压测
C3 依赖厂商编译器、kernel 或 Runtime 准备明确输入、验收标准和问题单交给厂商
C4 当前版本不支持或不应开放 不进入普通推理产品面

当前最重要的边界是:生产系统不是把 llama.cpp 的 GGML 节点逐个下发 NPU,而是由 CalRT 加载预编译的 prefill/decode 整图。新模型适配的主线因此是“模型导出和编译成 calbin + llama.cpp 运行时契约适配”,不是简单增加一个 GGML backend 算子。

2. 先建立三套基线

在申请 NPU 编译前先冻结三套基线,避免后续把模型误差、编译问题和服务问题混在一起。

2.1 模型语义基线(C0/C2

归档以下不可变信息:

  • Hugging Face 仓库、revision/commit、license 和所有权重 SHA-256。
  • config.json、generation config、tokenizer、chat template、special token ID。
  • 层数、hidden size、Q/KV heads、head dim、FFN/MoE 参数、vocab、RoPE 参数、训练 context。
  • 权重 dtype、tie embeddings、attention bias、norm epsilon、activation、sliding window。
  • 至少 100 组固定 prompt/token IDs,以及 FP32/BF16 参考 logits 和 greedy token 序列。

参考模型 tokenizer 声明 131072 context,但当前 calbin 固定为 40960。运行时可用上限必须取“calbin 容量、服务 slot 容量、tokenizer/模型训练上限”的最小值,而不能只读 tokenizer。

2.2 CPU/GPU 参考基线(C2

保留原生 Transformers 或上游 llama.cpp 基线,至少覆盖:

测试 目的
单 token embedding/第一层/末层 logits 快速定位模型结构或权重映射错误
prompt 长度 1、2、15、16、17 覆盖 D0=16 布局边界
255、256、257 和 4095、4096、4097 覆盖块边界与长序列算法切换
接近 context 上限 验证 RoPE、KV 地址和越界处理
多语言、代码、特殊 token、空 prompt 验证 tokenizer 和模板一致性
greedy 逐 token logits/top-k 将采样随机性排除在数值检查之外

2.3 当前 NPU 产品基线(C0

先在现有 Qwen3 calbin 上固化:

  • 服务、Runtime、driver、firmware、calbin 和源代码版本。
  • TTFT、prefill tokens/s、decode tokens/s、端到端 tokens/s。
  • H2D、NPU infer、D2H、BF16 转 FP32、sampling、排队各阶段耗时。
  • prompt/output 长度、并发、KV 占用、功耗/温度、错误和 reset 次数。
  • 输出 token 和日志的可复现性。

历史 CSV 的所谓 TTFT 是 predicted_ms + prompt_ms,且 model=deepseek 被脚本硬编码,不能直接作为新模型验收基线。应重写采集器,以客户端首字节、服务分层计时和 Runtime 计时三套时钟互证。

3. 端到端适配流水线

flowchart LR
    A["HF 权重和 tokenizer"] --> B["框架参考模型"]
    B --> C["规范化 ONNX"]
    C --> D["量化和校准"]
    D --> E["算子清单与融合"]
    E --> F["芯粒切分和内存规划"]
    F --> G["calcc/codegen/kernel"]
    G --> H["calbin 和 golden"]
    H --> I["CalRT 冒烟与数值验收"]
    I --> J["llama.cpp/服务接入"]
    J --> K["正确性、性能、并发、稳定性"]

阶段 0:选择第一个新模型(C2

优先选一个 dense decoder-only 模型,而不是再选 MoE。建议满足:

  • 架构接近 Llama/QwenRMSNorm、RoPE、GQA、SwiGLU 均为常见形式。
  • 1B 到 7B,单芯粒参数和 KV 明显能放下。
  • 不使用 sliding-window、MLA、自定义 attention、视觉 encoder 或动态 control flow。
  • vocab 不大于当前 151936context 先选 4K/8K。
  • 有公开参考权重和标准 benchmark。

这样能先验证导出、量化、编译、打包和服务契约,减少同时调试 MoE route、双芯粒通信和长上下文的变量。

阶段 1HF/tokenizer 固化(C2

执行项:

  1. 下载到内容寻址目录,生成文件清单和 SHA-256。
  2. 将 tokenizer 输出固定成整数 token IDs,记录 BOS/EOS/PAD、chat template 和 add-special-tokens 行为。
  3. 建立 greedy 解码脚本,每一步保存 top-20 token ID/logit。
  4. 导出模型结构 manifest,不依赖文件名猜测模型。
  5. 明确 context、batch 和 KV dtype 的产品目标。

交付物建议:

model-source/
  manifest.json
  config.json
  tokenizer/
  weights.sha256
reference/
  prompts.jsonl
  token_ids.npz
  logits_bf16_or_fp32.npz
  greedy_tokens.jsonl

阶段 2:规范化 ONNX(C2 准备,C3 最终兼容)

我方可以用 PyTorch/Transformers 导出 ONNX 并做标准检查,但无法确认 calcc 的私有输入 dialect。应同时保存原始 ONNX 和交给厂商的规范化 ONNX。

至少明确下列逻辑输入:

  • token IDs 或 input embeddings。
  • position IDs。
  • attention mask 或等价 sequence-length CSR。
  • past K/V 和 present K/V,或厂商 Runtime 管理的 KV 句柄。
  • prefill 与 decode 的动态轴/固定 shape。

推荐拆分为两个契约:

子图 输入 shape 输出 目标
prefill [B,S]S 在约定范围内 最后有效 token logits + KV 吞吐和 TTFT
decode [B,1] 每 batch logits + 更新 KV 每 token 时延和并发吞吐

导出后执行 ONNX checker、shape inference、常量折叠前后等价性,并生成逐 op type 计数。任何自定义 op 都要写明语义、shape、dtype、广播和误差要求。

阶段 3:量化和校准(C3

参考 calbin 使用 dynamic W8A8/W4AF16 混合量化,但现有资料没有量化器或量化规则。厂商必须提供可重放的配置和容器。

建议的第一轮策略:

模块 首轮 dtype 原因
embedding、norm、RoPE BF16 敏感且占比不高
Q/K/V/O、dense FFN W8A8 先追求稳定可验收
lm_head W8A8 或 BF16 对照 大 vocab 对精度和 D2H 都敏感
KV cache BF16 当前已确认基线
MoE experts dense 成功后再试 W8A8/W4AF16 隔离 route 与量化误差
router/gate BF16 或高精度 W8A8 top-k 翻转会放大输出差异

校准集必须覆盖真实语言、代码、长上下文、边界 token 和异常输入。验收不应只有最终文本;至少比较 layer probes、logits cosine/最大绝对误差、top-k overlap、greedy token 一致率和任务指标。

阶段 4:算子盘点与支持矩阵(C2/C3)

从 ONNX 自动生成:

  • op type、数量、shape family、dtype 和常量/动态属性。
  • 每个 op 的 producer/consumer 和 fusion 候选。
  • 是否有 NPU kernel、是否 CPU fallback、是否需要新 kernel。
  • prefill/decode 是否共享实现,动态 shape 是否受支持。

对 decoder-only dense 模型,优先确认:embedding、RMSNorm、MatMul/GEMM、RoPE、Flash Attention、SiLU、Mul、Add、KV scatter/gather 和 lm_head。对 MoE 额外确认 router、top-k、dispatch、grouped GEMM、combine/reduce。

现有 Qwen3 图提供一个可对照的编译结果:prefill 1496 个节点,decode 1398 个节点;每个图含 48 个 Flash Attention、144 个 grouped MoE dense、193 个 fused RMSNorm 和 193 个 quantized fused matmul/add。它证明这些融合形态存在,但不提供复现这些融合的 pass 或 kernel 源码。

阶段 5:图融合与 kernel 优化(C3)

优先级按端到端收益排序:

  1. RMSNorm + quantize + GEMM,减少中间 BF16/INT8 往返。
  2. QKV projection + RoPE + KV scatter,减少 layout conversion。
  3. Flash Attention,分别优化短 prefill、长 prefill 和单 token decode。
  4. SwiGLU gate/up 融合及 down projection + residual。
  5. MoE top-k + dispatch + grouped GEMM + combine。
  6. lm_head + top-k/sampling,减少每 token 303872 B BF16 logits D2H。

每个融合必须提供:未融合 golden、融合 golden、支持 shape/dtype、SRAM/DRAM 用量、数值误差、fallback、性能和故障边界。不要仅以 kernel 微基准判断收益;还要测跨芯粒同步、H2D/D2H 和服务排队。

阶段 6:芯粒分配(C3

编译输入应显式包含:芯粒数、每芯粒 CalCore 数、DRAM/SRAM 容量、互联带宽、参数/KV/工作区预算和 batch/context 组合。

候选切分:

方案 适合场景 主要风险
单芯粒 小型 dense 模型、多模型并存 单芯粒 kernel 吞吐和容量
逐层 tensor parallel 大 dense/MoE;当前 Qwen3 方案 每层 gather/reduce 和 D2D
pipeline parallel 层数多、microbatch 足够 单请求气泡、KV/调度复杂
expert parallel MoE 且专家权重/路由适合分布 all-to-all、负载不均、热点专家
混合 TP+EP/PP 大模型高并发 编译器和 Runtime 复杂度最高

当前 Qwen3 两芯粒都执行 48 层并各持有 128-expert 权重分片,是 tensor parallel,不是 expert parallel。新的 MoE 方案不能把当前文件名中的 2_chips 当成已经支持 EP 的证据。

阶段 7:内存规划与 calbin 生成(C3)

厂商输出必须包含:

  • param_blk*.bin 及 chip mask。
  • 全局和子模型 memory reservation。
  • prefill/decode I/O、CSR、ping/pong 地址和 shape/dtype。
  • 每芯粒 profile/op list、op I/O 地址图。
  • CPU/PLD command binaries 与可读文本。
  • CCU ELF、必要的 dynamic D2D helper。
  • 编译器/TVM/kernel/oplib commit、命令、配置、输入 ONNX hash。
  • golden input/output 或可计算 hash。

容量验算至少包括:

parameter_bytes_per_chip
+ KV_bytes_per_chip(batch, context, dtype)
+ peak_workspace_per_chip
+ IO_and_command_buffers
+ allocator_guard
<= physical_usable_memory_per_chip

BF16 KV 的基础公式:

KV bytes = layers * kv_heads * 2(K,V) * head_dim * context * batch * 2 bytes

参考模型为 3.750 GiB 总 KV、1.875 GiB/芯粒。S8 V-cache 若只压 V,节省量不能简单按整个 KV 减半,必须按实际 K/V dtype 和布局重算。

阶段 8calbin 静态验收(C2/C3

在上卡前运行一个无设备解析器:

  • 所有 manifest 文件存在,hash 和 size 正确。
  • 只允许明确的 prefill/decode/kv_update 类型。
  • 模型名精确匹配,不用 substring。
  • batch、context、vocab、chip mask、dtype 与服务 manifest 一致。
  • tensor 地址不越过 reservation,参数 offset 不越过文件。
  • ping/pong buffer 不发生不允许的别名。
  • device_memory_required 使用可解释单位。
  • prefill metadata 不能错误标成 llm_decode

当前产物中的 device_memory_required: 16777216 MB 单位明显不合理,prefill metadata 还存在类型标记问题。新产物必须把这两项作为阻断式检查,而不是继续容忍。

阶段 9CalRT 独立冒烟(C0/C1/C2

先绕过 llama.cpp,写一个最小 C++ runner

  1. CreateVDevice(PCIE),检查 Status()、Runtime/firmware 信息。
  2. CreateCalbin(),打印版本、模型列表和 LLM info。
  3. configure(),记录时间和内存占用。
  4. 按精确 model name 创建 InputBuf/OutputBuf。
  5. 填 token/position,设置 cur_seq_lenpast_kv_cur_seq_len
  6. 提交 prefillWait(),比较 golden logits。
  7. 连续提交 decode,逐 token 比较参考。
  8. 覆盖 KV allocate/apply/free/clear 和容量不足。
  9. 在隔离测试中再验证 parallel mode、fixed ping/pong 和状态机。

正确性门槛建议:解析/配置零错误;所有 tensor shape/dtype 完全一致;greedy 序列达到约定一致率;错误路径有限时返回,不得无限忙等或 abort() 整个服务。

阶段 10llama.cpp 与服务接入(C2

不要继续复制当前适配层中的硬编码。新增一个版本化 manifest 和严格 adapter

schema: 1
architecture: qwen3
calbin_version: ...
runtime_abi: 0.7.6
context: 40960
batch: 1
vocab: 151936
models:
  prefill: exact-prefill-name
  decode: exact-decode-name
tensors:
  token: inputs[0]
  position: inputs[1]
  logits: outputs[0]
csr:
  current: cur_seq_len[0]
  past_current: past_kv_cur_seq_len[0]

接入时必须:

  • 将 calbin 能力作为真实 n_ctx/n_batch/n_ubatch 来源。
  • 启动时校验 vocab、tokenizer、batch、context、model type 和 logits shape。
  • 去掉 output tile 的 D0=16 业务硬编码,读取 metadata/layout。
  • 给每个 in-flight job 独占或引用计数的 input/output/backing buffers。
  • 用 RAII 管理 MapBuf/UnMapBuf、slice reset、output completion 和 KV 生命周期。
  • 将 Runtime 错误映射为请求级、模型级和设备级故障,不直接 abort()
  • 修复非 USE_CALRT 构建中无条件使用 cal_ctx 的代码。
  • 将 sampling 与整 vocab logits 转换单独计时。

阶段 11:测试金字塔(C2

层级 内容 通过标准
静态 manifest、hash、地址、shape、单位 全部阻断项通过
单 op kernel/fusion golden、边界 shape 达到数值和性能门槛
子图 prefill/decode/KV 与参考逐步对齐
Runtime sync/async/ping/pong/error/recovery 无挂死、无 buffer 复用错误
服务 tokenizer、slot、stream、cancel、context shift API 行为正确
并发 1/2/4/8/16 请求、混合长短 prompt 无串扰,公平性可控
稳定性 1h/8h/24h soak、反复加载卸载 无持续内存增长或设备失联
性能 TTFT/TPOT/吞吐/功耗/利用率 相对冻结基线达标

测试矩阵必须同时固定 prompt tokens、generated tokens、并发和采样参数。报告 P50/P90/P99,不只报告平均值。

4. Dense-first 到 MoE 的两阶段路线

4.1 第一阶段:小型 dense 闭环

目标不是最高速度,而是第一次可重复地完成:

HF -> ONNX -> quantization -> calcc -> calbin -> golden -> CalRT -> llama.cpp -> server

建议产出单芯粒 batch 1 和 batch 4 两套 decode calbin。单芯粒验证多模型常驻的基础,batch 4 验证硬件 batch 和 buffer/KV 调度。首轮不做 NPU sampling,不做多物理板,不做复杂 context shift。

4.2 第二阶段:MoE 与双芯粒

在 dense 闭环稳定后增加:

  1. router logits 和 top-k 的高精度 reference。
  2. token-to-expert 分布、热点专家和负载不均指标。
  3. grouped GEMM 的 token bucket 边界。
  4. 当前 tensor parallel 与候选 expert parallel 的 A/B calbin。
  5. 跨芯粒有效字节、同步等待和每层时间。
  6. W8A8 与 W4AF16 对 router/expert 的分层消融。

只有 vendor compiler 能改变当前专家权重和计算的芯粒分配,因此 EP 属于 C3,不是修改 llama.cpp 调度就能实现。

5. 算子优化的实验方法

每次只改变一个编译或 Runtime 因素,并保存同一输入下的完整结果:

因素 A/B 例子
kernel/fusion fused vs unfused RMSNorm+GEMM
quantization W8A8 vs W4AF16BF16 router 对照
attention standard vs FA;短/长 prompt 分桶
partition single-chip vs TP2TP2 vs EP2
batch 1/4/8/16 decode calbin
KV BF16 vs S8 V-cacheshift on/off
output full logits D2H vs NPU top-k/sampling
runtime immediate Wait vs ping/pong in-flight=2

每个实验记录模型 hash、calbin hash、编译 commits、driver/firmware/Runtime、环境温度、运行顺序和原始 trace。性能提升必须同时给出数值回归结果;吞吐提升但 P99、正确性或恢复能力恶化时不能直接合入。

6. 厂商必须补齐的工具和契约

要让我们具备独立新模型适配能力,至少需要:

  1. 固定 digest 的 compiler container,内含 calcc、TVM、codegen、oplib 和 kernel。
  2. 当前 Qwen3 从 ONNX 到 calbin 的完整命令、配置和输入 ONNX。
  3. 支持 op/shape/dtype/量化矩阵与 fallback 规则。
  4. 单芯粒和双芯粒 partition 约束、内存/通信 cost model。
  5. batch 4/8/16 与动态 batch 的支持方式。
  6. golden 生成、逐层 dump、trace/counter 的稳定 API。
  7. calbin/Runtime/driver/firmware 兼容矩阵和错误码说明。
  8. kernel 开发、profiling 和自定义 op 接入文档。
  9. metadata schema,包括单位、model type、地址和版本字段。
  10. 可发布的 cal-llm EngineCore SDK、API/ABI 和迁移说明;Git 历史分支不能替代 SDK。

7. 第一个新模型项目的完成定义

只有同时满足以下条件才算完成:

  • 所有输入、工具和产物可由固定版本容器重放。
  • calbin 静态检查通过,版本/shape/context/batch/tokenizer 无歧义。
  • 独立 runner 和服务端逐 token 正确性达标。
  • sync 和 async 路径都无挂死、越界、串扰或 buffer 生命周期问题。
  • batch 1 以及至少一个 batch > 1 的产物通过并发压测。
  • 1h、8h、24h 稳定性测试无持续资源泄漏。
  • TTFT、TPOT、吞吐、P99、功耗和精度都有可追溯报告。
  • 所有 C3 能力都有厂商版本、负责人、交付件和回归用例,不再依赖口头说明。