Files
calculet-npu-research-archive/reports/Calculet-NPU-模型产物与内存映射-20260802.md

290 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Calculet NPU 模型产物与内存映射
日期:2026-08-02
模型:Qwen3-30B-A3B dynamic W8A8/W4AF16,双芯粒,40960 contextFlash Attention
实施下钻:逐文件/逐字段 grammar、跨文件校验、严格拒绝条件和可执行检查器见《Calculet NPU calbin 产物字段字典与静态验收规范》及 `calculet_package_validate.rb`。本文保留容量和映射分析,字段字典负责新包验收。
## 1. 路径与范围
本文的模型根目录是:
```text
outputs/Calculet-NPU-analysis-bundle-20260801/remote-export/extracted/models-complete/models
```
该目录下只有一套完整模型。`models` 树共 68 个文件:模型目录内 67 个文件,外加一份重复放置的 `Qwen3-30B-A3B.vocab.gguf`。不能把这些文件解释为 68 个模型。
模型目录内文件总量为 17.456 GiB;加外层 vocab GGUF 后约 17.462 GiB。
## 2. 产物分类
| 分类 | 数量 | 大小 | 作用 |
| --- | ---: | ---: | --- |
| 参数分片 | 2 | 16.938 GiB | chip0/chip1 的权重和常量块 |
| Runtime 命令文本 | 16 | 402.541 MiB | prefill/decode、chip0/1、CPU/PLD、ping/pong 的可读命令 |
| Runtime 命令二进制 | 16 | 81.549 MiB | 与上述命令对应的可部署二进制 |
| CCU ELF | 8 | 23.225 MiB | prefill/decode x chip0/1 x ccu0/1 |
| CPU helper SO | 2 | 0.250 MiB | prefill 动态 D2D 的 x86/RISC-V helper |
| profile parts | 4 | 0.122 MiB | prefill/decode 在两个芯粒的算子列表 |
| op 地址映射 | 2 | 1.003 MiB | 每个 op 的输入、常量、输出地址/shape/dtype |
| tokenizer | 9 | 22.426 MiB | tokenizer、vocab、merges、config、GGUF |
| 其他 metadata | 8 | 0.051 MiB | 编译配置、内存保留、hparam、版本 |
参数之外的文件不是可丢弃的“日志”:它们共同描述了芯粒分配、地址计划、命令流、编译配置、golden hash 和可复现性。
## 3. 目录语义
### 模型根目录
| 文件 | 含义 |
| --- | --- |
| `param_blk0.bin` | chip mask 1,对应 chip0 参数分片 |
| `param_blk1.bin` | chip mask 2,对应 chip1 参数分片 |
| `model_memory_reserved_info.txt` | 全模型 DRAM/SRAM/sync 保留和参数映射 |
| `hparam.yaml` | golden/冒烟推理时的 prefill/decode CSR |
| `*_2_chips_40960_fa.yaml` | 原 ONNX 路径/hash、shape、模型/设备/编译配置、golden hash |
| `*_op_io_buf_ptr.yaml` | op 级地址、shape、dtype 和生产者关系 |
| `*.profparts` | 每芯粒执行的 op 顺序 |
| `version.py` | 产物版本辅助信息 |
| `__Tokenizer/` | tokenizer 和模型结构配置;不含 safetensors 权重本体 |
### Prefill/Decode 子目录
| 文件模式 | 含义 |
| --- | --- |
| `submodel_memory_reserved_info.txt` | 子模型类型、batch、芯片模式、I/O、CSR、文件芯片掩码 |
| `cpu_chip{0,1}_{ping,pong}.bin` | CPU 侧任务命令 |
| `pld_cpu_chip{0,1}_{ping,pong}.bin` | PLD/CPU 数据移动命令 |
| `*_runtime_cmds_*.txt` | 上述二进制的文本表示 |
| `ccu{0,1}_chip{0,1}.elf` | 每芯粒两个 CalCore/CCU 程序 |
| `libcpu_x86.so` | prefill 动态命令 helper,导出 `Process` |
| `libcpu_riscv.so` | 同一 helper 的设备侧/目标架构版本 |
Decode 没有 `libcpu_*.so`,与编译配置 `dynamic_d2d=false` 一致;Prefill 为 `dynamic_d2d=true`
## 4. 编译输入与配置还原
模型 YAML 记录了同一原始 ONNX 用两组 shape 编译:
```text
/st_scratch/STG/calq_export_models/llm/Qwen3-30B-A3B/Dynamic/Merge_full_layers/
Qwen3-30B-A3B-dynamic-W8A8-W4AF16-full_layers_merged.onnx
```
ONNX SHA-256
```text
316f06e7e316b1305486118fa7da65f816b61deb46de09a2f4fec6979530456c
```
编译版本:
- calcc`575c2d708e306a60f10bc34f5087477073b58caa`
- TVM`7aebd1c21fa67d942a1fd93e39e8a67007f91c0c`
- `with_codegen=true`
- `with_op_case=true`
- `with_c_oplib=true`
- `use_auto_alloc=true`
- `enable_flash_attention=true`
- `enable_one_token=true`
- chip 数 2CalCore 数 2/芯粒
`has_loaded_onnx=false``has_optimized_graph=false` 是本次编译配置状态,不代表产物未经图优化;结合最终融合 op,应理解为编译入口没有复用预加载/预优化中间对象。
当前归档中没有 ONNX 文件、calcc 可执行文件、pass 源码、oplib、kernel 源码或 compiler container。因此可以反推配置,不能重放编译。
## 5. 子模型 I/O 与 CSR
### Prefill
| 对象 | Shape | Dtype | 地址/大小 |
| --- | --- | --- | --- |
| `inputs[0]` token | `[1,40960]` | INT32 | ping/pong `0xc0000000/0xc0028000`163840 B |
| `inputs[1]` position | `[1,40960]` | INT32 | ping/pong `0xc0050000/0xc0078000`163840 B |
| `outputs[0]` logits | `[1,1,151936]` | BF16 | ping/pong `0x367b21000/0x367b6c000`303872 B |
| `cur_seq_len[0]` | CSR | UINT32 | offset `0x1104` |
| `past_kv_cur_seq_len[0]` | CSR | UINT32 | offset `0x1144` |
Prefill 虽按 40960 最大输入分配,llama 端通过 `SliceTensor()` 只传实际 token/position,并只回传末 token 的 logits tile。
### Decode
| 对象 | Shape | Dtype | 地址/大小 |
| --- | --- | --- | --- |
| `inputs[0]` token | `[1,1]` | INT32 | ping/pong 同址 `0x08e61800/0x08e61c00`4 B |
| `inputs[1]` position | `[1,1]` | INT32 | ping/pong `0xc0000000/0xc0001000`4 B |
| `outputs[0]` logits | `[1,1,151936]` | BF16 | ping/pong `0xc0002000/0xc004d000`303872 B |
| `cur_seq_len[0]` | CSR | UINT32 | offset `0x1104` |
| `past_kv_cur_seq_len[0]` | CSR | UINT32 | offset `0x1144` |
`hparam.yaml` 的上限测试设置为:prefill `cur=40959,total=40959`decode `cur=1,total=40960`
## 6. 参数映射
### 文件大小
| 文件 | 精确字节 | GiB | 芯片掩码 |
| --- | ---: | ---: | --- |
| `param_blk0.bin` | 9,404,557,312 | 8.759 | `0x1`chip0 |
| `param_blk1.bin` | 8,782,227,456 | 8.179 | `0x2`chip1 |
| 合计 | 18,186,784,768 | 16.938 | 双芯粒 |
### 按 memory reservation 段求和
| 芯粒 | DRAM 映射 | SRAM 窗口映射 | 合计与文件关系 |
| --- | ---: | ---: | --- |
| chip0 | 9,389,477,888 B8.745 GiB | 15,079,424 B14.381 MiB | 与 `param_blk0.bin` 精确闭合 |
| chip1 | 8,767,148,032 B8.165 GiB | 15,079,424 B14.381 MiB | 与 `param_blk1.bin` 精确闭合 |
SRAM 常量不是额外文件;它们是参数分片中的指定 offset,在 configure 时放入 SRAM 地址窗口。
## 7. 全局保留区
`model_memory_reserved_info.txt` 给出的逻辑地址计划:
| 类型 | 起始地址 | 大小 | 换算 |
| --- | --- | ---: | ---: |
| DRAM reserved | `0xc0000000` | 15,113,064,448 B | 14.075 GiB |
| SRAM reserved | `0x08000000` | 17,662,336 B | 16.844 MiB |
| sync reserved | `0x0a080000` | 20 B | 20 B |
这些地址在参数段中按 chip mask 1/2 分别出现,因此最合理的解释是同一虚拟地址计划应用于每颗芯粒。若成立,双芯粒逻辑 reservation 约为 28.150 GiB DRAM + 33.688 MiB SRAM。该点应让厂商书面确认。
`device_memory_required: 16777216 MB` 的单位明显错误。若数值实际是 KiB,则为 16 GiB/芯粒;若按字段 MB 解释则完全不可能。
## 8. KV Cache
模型配置:48 层、4 KV heads、head dim 128、BF16、最大 40960、batch 1K/V 各一份。
```text
KV bytes
= layers x KV_heads x (K + V) x head_dim x context x BF16_bytes
= 48 x 4 x 2 x 128 x 40960 x 2
= 4,026,531,840 bytes
= 3.750 GiB
```
当前 tensor parallel 将 4 个 KV heads 分成每芯粒 2 个,所以每芯粒为:
```text
2,013,265,920 bytes = 1.875 GiB
```
Decode op 地址图中最大的 activation 连续区间是:
```text
0xc00a0000 .. 0x1380a0000 = 2,013,265,920 bytes
```
它与每芯粒 KV 公式精确相等,是 KV 区间的直接产物证据。
CalRT 0.7.6 的 KV 数据结构支持 BF16 K-cache、BF16/S8 V-cache 和 batch>1 的不同 K/V layout,但当前 calbin 是 BF16、batch 1。不能仅通过 Runtime 开关把现有模型变成 S8 V-cache。
## 9. Tensor 地址并集
`*_op_io_buf_ptr.yaml` 的所有输入/输出区间去重并合并后:
| 图 | 常量覆盖 | activation 覆盖 | input 覆盖 | 全部地址并集 |
| --- | ---: | ---: | ---: | ---: |
| Decode | 8.758 GiB | 1.875 GiB | 8 B | 10.634 GiB |
| Prefill | 8.758 GiB | 5.332 GiB | 0.000305 GiB | 14.091 GiB |
解释规则:
- “覆盖”是地址区间并集,不是同时活跃的峰值内存。
- 常量、activation 可能在边界处相邻或复用,不可直接相加。
- Decode activation 基本就是 1.875 GiB KV 加少量 I/O/workspace。
- Prefill 除 KV 外还有约 3.456 GiB 的长序列 activation/workspace 地址区。
- Prefill 全部并集 14.091 GiB 与 14.075 GiB DRAM + 16.844 MiB SRAM 基本闭合,说明地址解析口径正确。
上述值来自 `work/analyze_calculet_artifacts.rb`,原始计算输出在 `work/calculet-artifact-analysis.tsv`
## 10. 双芯粒算子分配
| 图 | chip0 profile 行 | chip1 profile 行 | 差异 |
| --- | ---: | ---: | --- |
| Decode | 1254 | 1253 | embedding 只在 chip0 |
| Prefill | 1352 | 1351 | embedding 只在 chip0 |
两边都有:
- 193 个 fused RMSNorm。
- 193 个 quantized fused matmul/add。
- 144 个 MoE grouped dense。
- 96 个 KV scatter、RoPE、residual。
- 48 个 Flash Attention、router pre/post、SiLU、multiply、reduce。
MoE grouped dense 的权重 shape 包含 128 experts,例如 decode 第一层:
```text
gate/up weight: (128, 24, 128, 16, 8) U8
down weight: (128, 128, 24, 16, 8) U8
active input: (8, 2048) BF16
```
两芯粒都保留 128 experts 的矩阵分片,当前不是 expert parallel。
## 11. Prefill 动态 D2D
静态 ping 命令文本统计:
| 芯粒 | `dataSize` 项 | 总描述量 | 64 KiB | 32 KiB | 1 KiB |
| --- | ---: | ---: | ---: | ---: | ---: |
| chip0 | 444,610 | 18.979 GiB | 245,760 | 122,880 | 75,968 |
| chip1 | 444,608 | 18.822 GiB | 245,760 | 122,880 | 75,968 |
chip0 另有一次 160 MiB 和一次 160 KiB 描述。Prefill helper `libcpu_x86.so` 的唯一业务导出是 `Process`;结合 `dynamic_d2d=true`,实际运行会按当前序列长度生成或裁剪任务。
因此后续优化应测:
- 实际 prompt 长度与 D2D 命令数/字节的关系。
- 每层 gather/reduce 中哪类数据占主导。
- 64 KiB/32 KiB 块的有效带宽和同步空洞。
- 动态 helper 的 CPU 时间是否计入 `framework unknown`
## 12. Metadata 质量问题
| 问题 | 当前证据 | 风险 |
| --- | --- | --- |
| Prefill `smodel_type=llm_decode` | prefill submodel memory 文件 | 不能可靠按类型调度 |
| tokenizer 131072 vs model 40960 | tokenizer_config 与 config/calbin | API 可能接受超上限请求 |
| `device_memory_required` 单位错误 | model memory 文件 | 自动 admission control 不可信 |
| `tarChipN` 为空 | CalRT 解析实测 | 拓扑自检不足 |
| workload 曾报告为 0/不一致 | parser 输出与文件字段 | 调度成本模型不可用 |
| 仅靠名称子串选模型 | llama-calrt.cpp | 多变体时可能选错 |
| tile `D0=16` 在 C++ 硬编码 | llama-calrt.cpp | 新编译器/layout 易回归 |
建议新增版本化 manifest,至少包含:schema version、model role、I/O 语义名、shape bucket、dtype/layout/tile、batch、context、KV layout、chip topology、compiler/runtime/driver compatibility、calbin hash、golden hash、量化方案和采样输出模式。
## 13. 容量推导与部署含义
在当前双芯粒大模型上:
- 参数和最大 KV 已占据大部分 32 GiB 板级 DRAM。
- Prefill 地址计划接近每芯粒 reservation 上限。
- 第二个同规模模型不可能同时常驻。
- 小型 embedding/reranker 是否可与主模型共存,必须基于 per-chip 可用 DRAM/SRAM 和 Runtime 是否支持多个已配置 calbin 实测。
- 动态卸载/加载的核心成本不是文件读取,而是参数 H2D、configure、ELF/命令部署、KV 清理和服务停顿。
对 batch N 的 KV 粗略容量:
```text
BF16 KV = 3.75 GiB x N (40960 context,全槽满载)
```
因此 batch 4 的最大上下文 BF16 KV 理论上需 15 GiB/板,仅 KV 就会挤压参数和 workspace。真正 batch 设计必须使用共享总 token budget、较短 slot context、paged/block KV 或 V-cache 量化,不能简单复制 batch=1 的 40960 全容量。
## 14. 可复算方法
工作区提供的只读分析脚本:
```bash
ruby work/analyze_calculet_artifacts.rb \
<model-dir>/*_op_io_buf_ptr.yaml \
<model-dir>/*prefill*/pld_cpu_chip0_runtime_cmds_ping.txt \
<model-dir>/*prefill*/pld_cpu_chip1_runtime_cmds_ping.txt
```
脚本只读取 YAML/命令文本,计算地址区间并集、命令条数、字节总量和块大小直方图,不修改模型产物。