36 KiB
Calculet NPU 双芯粒双模型独立服务可行性与实施规格
日期:2026-08-02
基线:CalRT 0.7.6、llama.cpp fd9bd632、一张 PCIe 板、两个芯粒、每芯粒两个 CalCore
目标:chip0 常驻模型 A,chip1 常驻模型 B;每个模型的 prefill 和 decode 全部在自己的芯粒完成,两个模型同时对外提供服务
0. 执行结论
这个目标需要先澄清“核心”的含义:
| “核心”指什么 | 当前结论 | 原因 | 状态 |
|---|---|---|---|
| 每芯粒内的一个 CalCore/CCU | 当前不可直接实现 | 完整图会为同一芯粒同时生成 ccu0、ccu1 程序;公开 API 没有 CalCore affinity、独立配置、内存配额或 reset domain |
C4(0.7.6 公共能力)/C3(需厂商新能力) |
| 板上的一颗芯粒 | 架构上可行,当前不能直接上线 | calbin 已有单/多芯粒描述和 chip mask,但缺少单芯粒完整模型产物,以及双 session/队列/KV/reset 的正式隔离契约 | C3 |
因此,建议把目标正式定义成:
chip0: Model A = A.prefill + A.decode + A.params + A.KV + A.workspace
chip1: Model B = B.prefill + B.decode + B.params + B.KV + B.workspace
不是:
CalCore0: Model A
CalCore1: Model B
推荐两条实现路径:
- 最短硬件验证路径:厂商组合 calbin。 由 compiler 在一个 calbin 内统一放入 A/B 四个子图,A 的所有 section 只带 chip0 mask,B 只带 chip1 mask;整个 calbin 只 configure 一次,再按
CalbinModel*提交。它最贴近 0.7.6 的“单 current calbin”模型,但能否真正双芯粒并行仍需厂商 runner 和 trace 证明,状态 C3。 - 正式产品路径:Runtime chip session。 Runtime 显式提供
chip_session/model_handle/job_handle,每颗芯粒独立 load、quota、queue、KV、health、drain、unload 和 reset。服务层再实现两个 ModelWorker 和一个 Router,状态 C3。
当前 Qwen3-30B-A3B 双芯粒产物不能通过删文件、改 mask 或只传 chipId 改成两个单芯粒模型;该产物本身是逐层双芯粒 tensor parallel。第一轮应选择 1B-3B、decoder-only、dense、4K/8K context 的模型,优先选择厂商已有算子覆盖、与现有模型架构接近的模型。
1. 状态和验收口径
| 状态 | 本文使用规则 |
|---|---|
| C0 | 当前源码、产物或样机记录直接证明 |
| C1 | 0.7.6 头文件/符号存在,但生产未接入或未测 |
| C2 | 我方能用已确认接口实现,仍需开发和硬件验收 |
| C3 | 需要厂商 compiler、Runtime、kernel 或新 calbin |
| C4 | 当前接口明确不支持,或不应作为产品路径 |
“两个模型同时服务”再分两层:
| 等级 | 定义 | 本项目目标 |
|---|---|---|
| S1 服务并存 | 两个 alias 同时 Ready,能同时接收请求,但板内可能串行执行 | 中间里程碑 |
| S2 硬件并发 | chip0/chip1 的计算区间真实重叠,联合吞吐接近两颗芯粒隔离运行之和 | 最终目标 |
只有 Router 同时暴露两个模型不能证明 S2。必须通过 per-chip trace、任务时间线和联合吞吐证明硬件重叠。
2. 当前证据为什么支持这个判断
2.1 板卡是一个 PCIe 设备,不是两个独立设备
保存的 lspci 只看到一项 Calculet 设备 01:00.0,由 calculet 驱动管理。CalRT 0.7.6 的 VirtualDevice 头文件也明确写着“temporary only support one physical device”,内部只有一个 m_phyDevices。
这意味着:
- 两颗芯粒是同一物理设备内部资源。
- 不能用“两个 PCIe device id + 两个进程”绕过 Runtime 隔离问题。
- 在厂商确认前,不允许两个进程分别打开同一设备并各自 configure 一个 calbin;全局配置、地址和 reset 可能互相覆盖。
2.2 CalCore 是编译器执行单元,不是应用可分配的模型槽
当前 prefill 和 decode 各包含四个 ELF:
ccu0_chip0.elf ccu1_chip0.elf
ccu0_chip1.elf ccu1_chip1.elf
根 manifest 对两个子图都声明:
device_cfg:
chip_nums: 2
calcore_num: 2
所以一个完整子图在每颗芯粒上会同时使用两个 CalCore。calcore_num: 2 只能证明 codegen 生成了两个执行程序,不能证明应用能把两个 host job 分别绑定到两个 CalCore。
公开 API 的 TASK_PING/TASK_PONG 是 job engine/bank 选择,不是 CalCore0/CalCore1 亲和性。SubmitJob() 也没有 core_id。
2.3 calbin 有芯粒描述能力,但不等于多模型隔离已经可用
0.7.6 头文件包含:
ModelChipMode_e { SCHIP, MCHIP }。CalbinModel::tarChipN。- 每个
CalbinSection_s::chipMask。 TaskInfo_s::m_taskChipArch的SINGLE_CHIP_TASK/MULTIPLE_CHIP_TASK。ReadMem/WriteMem(..., chipId)和 remote chip memory API。
这些是单芯粒编译和按芯粒部署的有力线索,但不能证明:
- 两个模型可以同时 configure。
- 两个 job queue 独立。
- KV manager 按模型/芯粒隔离。
- reset 一颗芯粒不会影响另一颗。
- 两个线程或进程可以同时提交而无全局锁。
2.4 0.7.6 仍是单 current-calbin 设备状态
CalrtDevice 当前只有:
Calbin *pmCurrentCalbin
uint64_t mCurCalbinHash
SetConfigModel(Calbin*)
GetCurrentCalbinOnDevice()
configure(VirtualDevice*, Calbin*) 对整个 calbin 生效,SubmitJob(model, input, output, engine) 虽选择子模型,但没有 chip_session、model_handle、memory_quota 或 reset_scope。
这正是组合 calbin 可能成为短期路径的原因:A/B 可以由 compiler 放进同一个全局 calbin;也是不能由应用随意加载两个独立 calbin 的原因。
2.5 当前生产适配层只有一个设备、一个 calbin、一个 KV manager
calrt_context 只持有一份:
VirtualDevice
Calbin
KvManager
prefill/decode backing vectors
当前请求路径按第一个 ubatch 选择 prefill/decode,创建 buffer,infer() 后立即 Wait();多个 sequence 仍逐个提交。生产代码没有 model instance、chip worker、独立 KV namespace 或双模型 router。
历史 origin/runtime_replace 分支也只有一个 CalrtEngineConfig.calbin_path 和一个 EngineCore 实例,不能作为双模型已实现的证据。
3. 当前模型为什么不能直接拆开
当前 Qwen3 产物的关键事实:
| 项目 | 当前值 | 含义 |
|---|---|---|
| 参数块 | 16.938 GiB 合计 | chip0 8.759 GiB,chip1 8.179 GiB,是张量分片,不是两套完整权重 |
| BF16 KV | 3.750 GiB 合计 | 与 48 层、4 KV heads、40960 context 对应 |
| DRAM reservation | 14.075 GiB/芯粒地址计划(推断) | 已接近可能的单芯粒本地容量,物理口径仍需厂商确认 |
| 图分配 | 两颗芯粒都覆盖 48 层 attention/MoE | 当前是逐层 tensor parallel |
| 通信 | 每层 gather/reduce/sync;prefill 有 dynamic D2D | 删除另一芯粒后图不完整 |
| batch | 1 | 不能由服务端聚合自动变成硬件 batch |
以下做法全部禁止:
- 把
param_blk0.bin当成 chip0 的完整模型,把param_blk1.bin当成 chip1 的另一个模型。 - 删除
chip1ELF/command 后运行 chip0。 - 手工把 section 的 mask 从
3改成1或2。 - 把两套模型目录拼到一起并改子图名。
- 创建两个进程分别 configure 同一物理板。
- 用
ReadMem(..., chipId)推断 job 也能按 chip 独立提交。
这些操作会破坏权重、通信、同步、地址和命令流的一致性,轻则配置失败,重则错误输出、DMA 越界或整板 reset。
4. 推荐目标架构
flowchart LR
C["OpenAI-compatible clients"] --> R["Model Router"]
R -->|"model=A"| WA["ModelWorker A"]
R -->|"model=B"| WB["ModelWorker B"]
WA --> QA["A admission/queues"]
QA --> KVA["A KV manager"]
QA --> BFA["A buffer pool"]
KVA --> SA["chip0 session"]
BFA --> SA
WB --> QB["B admission/queues"]
QB --> KVB["B KV manager"]
QB --> BFB["B buffer pool"]
KVB --> SB["chip1 session"]
BFB --> SB
SA --> DS["One DeviceSupervisor / one board"]
SB --> DS
DS --> NPU["Calculet NPU: chip0 + chip1"]
硬约束:
- 进程内只有一个
DeviceSupervisor拥有板卡生命周期。 - 每个 ModelWorker 只访问自己的 session、KV、buffer、队列和 generation。
- Router 按稳定
model_alias路由,不把chip_id暴露给普通客户端。 - 默认模型 A/B 都各自包含 prefill 和 decode,sequence 不跨芯粒迁移。
- 任何提交都带 model/session 身份,不能依赖“当前 calbin”隐式选择。
- 如果 Runtime 只能组合 calbin,DeviceSupervisor 负责唯一 configure;两个 worker 只能借用其中已绑定 chip mask 的子模型。
5. 三条实现路径对比
| 路径 | 0.7.6 适配度 | 改动 | 优点 | 主要风险 | 建议 |
|---|---|---|---|---|---|
| A. 厂商组合 calbin | 最高 | compiler 生成 A/B 统一地址计划;我方单 DeviceSupervisor + 双 worker | 避开多 current-calbin;最快证明芯粒并发 | unload 粒度可能是整包;queue/reset 是否隔离未知 | 第一优先 PoC,C3 |
| B. Runtime chip session | 需新版本/API | Runtime、driver 和我方适配层共同升级 | 生命周期清楚,可独立 load/unload/quota/reset | 厂商开发量最大,ABI/兼容性风险 | 正式产品目标,C3 |
| C. 两张物理板各一模型 | 0.7.6 头文件仍称单物理设备 | 新多设备选择 API,或两台主机 | 隔离最强,运维简单 | 需要第二张板;0.7.6 多板 API 不完整 | 业务兜底,C3/C4 |
不采用“同一板两个进程各占一个芯粒”。在芯粒被 Runtime 显式虚拟化成可独占设备之前,这种方案没有安全边界。
6. 路径 A:组合 calbin 的精确要求
6.1 逻辑内容
一个组合包至少包含四个业务子图:
model_a.prefill -> tarChipN=[0], SCHIP
model_a.decode -> tarChipN=[0], SCHIP
model_b.prefill -> tarChipN=[1], SCHIP
model_b.decode -> tarChipN=[1], SCHIP
以及每个模型独立的:
- 参数块和参数映射。
- KV 地址、容量、sequence id namespace。
- prefill/decode workspace。
- input/output/CSR metadata。
- chip-local
ccu0/ccu1ELF。 - CPU/PLD ping/pong command。
- golden input/output、tokenizer、模型配置和 hash。
“A 在 chip0”不代表 A 只使用 ccu0。单芯粒 A 仍可由 compiler 同时使用 chip0 的两个 CalCore;B 同理。
6.2 compiler 必须统一解决的内容
- A/B 的虚拟地址允许在不同芯粒使用相同数值,但 mask、DMA route 和 host mapping 必须无歧义。
- 同一芯粒内 A.prefill/A.decode 可以按生命周期复用 workspace;A 与 B 不能共享可写状态。
- 两模型的 KV base、sync unit、job id 和 completion 信息必须独立。
- A 的任何 section 不得带 chip1 mask;B 反之。
- 不得生成跨模型或跨芯粒 gather/reduce/D2D。
- prefill/decode 的 tensor shape、dtype、CSR、batch、context 必须分别写入 manifest。
- 组合包必须由 compiler/packer 正式生成,不能由我方脚本拼接。
6.3 建议目录合同
dual-model-calbin/
manifest.yaml
model_memory_reserved_info.txt
model-a/
prefill/...
decode/...
params/...
golden/...
model-b/
prefill/...
decode/...
params/...
golden/...
tokenizer/model-a/...
tokenizer/model-b/...
checksums.sha256
compatibility.json
build-provenance.json
实际文件名可沿用厂商 schema,但 manifest 必须能无字符串模糊匹配地确定:alias -> prefill/decode -> chip -> profile。
6.4 组合 calbin runner
PoC runner 必须只做以下固定流程:
CreateVDevice(PCIE)
CreateCalbin(combined_path)
validate exact four model names and chip targets
configure(vdev, combined_calbin) exactly once
create A/B independent input/output buffers
run A golden, run B golden
run A+B concurrent submissions with separate buffers
Wait both, verify output and per-chip trace
drain, ResetConfiguration, release
SubmitJob/infer 没有 chip 参数,所以 chip 选择必须完全来自经过签名/校验的 CalbinModel metadata。runner 启动时发现任何模型 mask 越界、跨 chip section 或名字重复,应在 configure 前退出。
7. 路径 B:Runtime chip session 的最小 API 契约
建议厂商提供等价于下面的能力;具体命名可以不同,但语义不能缺失:
struct ChipSessionOptions {
uint32_t chip_id;
uint64_t dram_quota_bytes;
uint64_t sram_quota_bytes;
uint32_t max_inflight;
};
Topology QueryTopology(DeviceHandle);
ChipSessionHandle CreateChipSession(DeviceHandle, ChipSessionOptions);
ModelHandle LoadModel(ChipSessionHandle, const CalbinPath&, LoadOptions);
JobHandle Submit(
ChipSessionHandle,
ModelHandle,
GraphKind, // prefill | decode
InputBufferHandle,
OutputBufferHandle,
JobOptions);
JobStatus Query(JobHandle);
Status Wait(JobHandle, Deadline);
Status Cancel(JobHandle); // 必须明确是否只取消交付,还是终止设备执行
Status Drain(ChipSessionHandle, Deadline);
Status Unload(ModelHandle);
Status ResetSession(ChipSessionHandle);
MemoryUsage GetMemoryUsage(ChipSessionHandle);
Health GetHealth(ChipSessionHandle);
7.1 必须写进正式契约的语义
| 契约 | 必须回答的问题 |
|---|---|
| 地址空间 | 两 session 可否使用相同 vaddr;IOMMU/DMA 如何区分;host mapping 生命周期 |
| configure | chip0 load 是否会改变 chip1 的 current model、命令表或 sync unit |
| 提交 | 两线程并发 Submit 是否线程安全;每芯粒最大 in-flight;完成顺序 |
| 队列 | 全板共享队列还是 per-chip queue;是否存在全局 head-of-line blocking |
| KV | key 必须含 (session, model_generation, sequence_id);失败后的 commit/rollback |
| 内存 | quota 的硬/软语义;allocation 失败是否只影响本 session |
| reset | CCU/session/chip/device 四种 reset 各影响哪些任务、KV、参数和另一芯粒 |
| unload | 只释放目标 model/session;在途 DMA 未完成时必须拒绝或 drain |
| 错误 | timeout/crash/invalid calbin 的污染范围和恢复顺序 |
| 观测 | per-chip 温度、功耗、频率、内存、queue、job、trace 的单位和时钟 |
若 Runtime 只有 ResetConfiguration() 和整板 Reset(),两个模型可以做到并发服务,但不能宣称具备强故障隔离;任一模型故障可能导致两个 alias 同时摘流。
8. 模型选择和内存预算
8.1 容量不是只看参数文件
每颗芯粒的稳态预算至少为:
M_required = W_compiled
+ KV(batch, context)
+ workspace_peak(prefill, decode)
+ code_and_commands
+ input_output_staging
+ runtime_metadata
+ allocator_fragmentation
+ safety_guard
BF16 KV 的理论下界:
KV_bytes = 2(K,V)
* n_layers
* n_kv_heads
* head_dim
* context_tokens
* batch_or_active_sequences
* bytes_per_element
真实值还受 layout、对齐、batch lane、page/block 和 compiler reservation 影响,最终只能以新 calbin 的 metadata 加设备实测为准。
8.2 推荐安全线
在厂商没有提供可靠 per-chip memory API 前:
- 不以
GetMemoryUsage()的总量字段做容量承诺。 - 静态
M_required不超过厂商确认的本地 DRAM 的 75%-80%。 - 至少保留
max(1 GiB, 15% local DRAM)作为 Runtime、碎片、瞬时 workspace 和恢复余量。 - 以 prefill 峰值 workspace 而非 decode 常态决定是否可部署。
- A/B 分别验算,不能拿整板 32 GiB 作为任一芯粒可独占容量。
当前 14.075 GiB/芯粒 reservation 已说明大模型地址计划非常紧;当前 Qwen3 不适合作为单芯粒完整模型候选。
8.3 第一轮模型范围
推荐:
| 维度 | 第一轮选择 | 原因 |
|---|---|---|
| 架构 | decoder-only dense | 去掉 MoE router/expert 和跨芯粒通信变量 |
| 参数规模 | 1B-3B | 为权重、KV、workspace 和并发留余量 |
| context | 4K,随后 8K | 快速闭环,再扩大 KV |
| batch | B1 | 先证明双芯粒隔离;B4/B8 是后续独立课题 |
| 量化 | 厂商已有稳定 W8A8;必要时比较 W4 | 避免模型、量化、双模型同时引入未知量 |
| tokenizer | 各模型独立 | 避免 alias 路由到错误词表 |
若希望先隔离“不同模型算子覆盖”变量,Phase 2 应先把同一个小模型复制到两颗芯粒;通过后再换成不同模型 A/B。
8.4 容量验收表
每个模型必须提交并实测:
| 项 | 静态来源 | 运行验证 |
|---|---|---|
| 参数 | parameter map/文件区间 | load 前后 per-chip memory delta |
| KV/sequence | config 公式和 reservation | 1/2/... active sequence 增量 |
| workspace | compiler peak report | prefill/decode 峰值采样 |
| code/command | section 文件和映射 | configure delta |
| allocator 碎片 | 不可纯静态确定 | 100 次 load/unload 后回落 |
| guard | 项目策略 | OOM 前必须 admission reject,不允许设备 crash |
9. 每芯粒的 prefill/decode 调度
9.1 同一模型的基本顺序
一个 sequence 永远遵循:
Queued -> PrefillReady -> PrefillRunning -> DecodeReady
-> DecodeRunning -> DecodeReady ... -> Completed
prefill 成功后才提交第一次 decode;KV 更新使用 reserve -> submit -> commit/rollback。任务失败或取消不能立即复用仍被 DMA 引用的 buffer。
9.2 每个 ModelWorker 的队列
decode_ready_queue 高优先级,降低 TPOT
short_prefill_queue 例如 <=512 token
medium_prefill_queue 例如 <=4096 token
long_prefill_queue 低优先级但必须防饥饿
每个 worker 独立做 admission:
- 最大等待请求数。
- 最大等待 prompt token 数。
- KV 可分配量。
- 请求 deadline。
- 模型 context/batch profile。
- 当前 chip health 和 generation。
模型 A 过载只能返回 A 的 429/503,不应阻塞模型 B 的 admission。
9.3 长 prefill 对 decode 的影响
即使 A/B 在不同芯粒,单个模型内部仍可能被长 prefill 阻塞 decode。只有 compiler/Runtime 明确支持 chunked prefill、抢占或安全 ping/pong 时才能切分/重叠。第一版采用:
- decode 优先。
- 限制长 prefill 的 admission 和并发数。
- 不擅自把一个 prompt 拆成多个 prefill 图调用。
- 每颗芯粒初始
max_inflight=1;完成独立 buffer 和 ping/pong 验证后才升到 2。
9.4 两芯粒并发的四种干扰组合
必须分别测:
| chip0 | chip1 | 主要观察 |
|---|---|---|
| A prefill | B prefill | DRAM/DMA/功耗峰值、TTFT |
| A prefill | B decode | 长任务是否拖慢 B 的 TPOT |
| A decode | B prefill | 对称性、共享队列偏置 |
| A decode | B decode | 稳态吞吐、带宽、温度、频率 |
两颗芯粒可能共享 PCIe、板级功耗/散热和部分 Runtime 锁,因此即使计算单元独立,也不能假定联合性能等于孤立性能简单相加。
10. 服务层实施规格
10.1 对外接口
保持 OpenAI-compatible model 字段路由:
GET /v1/models
POST /v1/chat/completions body.model = "model-a" | "model-b"
POST /v1/completions body.model = "model-a" | "model-b"
DELETE /internal/requests/{request_id}
GET /v1/models 只在对应 worker 完成 configure、golden 和 warmup 后返回 Ready。普通接口不暴露 chip id、calbin 路径、虚拟地址、寄存器或 reset。
10.2 内部对象
DeviceSupervisor
board_handle
topology
lifecycle mutex
chip_sessions[2]
board health/recovery
ModelWorker
model_alias
chip_id
model_generation
prefill/decode graph handles
tokenizer/config
admission controller
scheduler queues
KV manager
buffer pools
submit/completion workers
health and metrics
RequestContext
request_id/model_alias/generation
sequence_id/deadline/cancel
prompt/output counters
KV transaction
current JobContext
JobContext
session/model/graph/job handles
exclusive input/output/backing storage
CSR snapshot
submit/deadline/status/timings
10.3 锁和所有权
DeviceSupervisor.lifecycle_mutex只用于 load/unload/reset,不包住正常Wait()。- A/B 各有 scheduler mutex;绝不同时持有两个 worker mutex。
- JobContext 从 prepare 到 completion 独占 host backing memory。
- KV commit 发生在 OutputBuf 完成且输出有效之后。
- reset 增加 session/device generation;旧 generation 的 completion 只能被丢弃,不能写入新请求。
- Router 只保存 worker 的稳定引用,不直接持有 Runtime handle。
10.4 建议配置
schema: calculet-dual-model/v1
device:
type: pcie
expected_chips: 2
combined_calbin: /models/dual-a-b
models:
- alias: model-a
chip_id: 0
prefill_graph: model_a_prefill
decode_graph: model_a_decode
context_limit: 4096
max_inflight: 1
max_waiting_requests: 64
- alias: model-b
chip_id: 1
prefill_graph: model_b_prefill
decode_graph: model_b_decode
context_limit: 4096
max_inflight: 1
max_waiting_requests: 64
recovery:
allow_session_reset: false
restart_process_on_unknown_dma: true
loader 必须将配置与签名 manifest 交叉验证。chip、graph、context、batch、tensor 或 hash 不一致时启动失败,不能自动猜测或按子字符串匹配。
11. 生命周期和故障状态机
11.1 单 worker 生命周期
stateDiagram-v2
[*] --> Unloaded
Unloaded --> Validating: load
Validating --> Configuring: manifest/hash/compat pass
Validating --> Failed: reject
Configuring --> Warming: configure succeeds
Configuring --> Failed: configure fails
Warming --> Ready: golden + warmup pass
Warming --> Failed: mismatch/fault
Ready --> Draining: unload/replace/session fault
Draining --> Unloading: in-flight=0
Draining --> Recovering: deadline/unknown DMA
Unloading --> Unloaded: memory released
Recovering --> Warming: scoped reset + reload
Recovering --> Failed: reset scope unknown/fails
11.2 组合 calbin 的特殊限制
组合 calbin 很可能只能整包 configure/reset。此时 A/B 的逻辑状态虽独立,但物理生命周期如下:
任一模型需要重新 configure
-> Router 同时停止 A/B admission
-> drain 两个 worker
-> 整包 ResetConfiguration
-> configure 新组合包
-> 分别 golden/warmup
-> 两者都 Ready 后恢复接流
不能在没有 Runtime 保证时只释放 A 的 Calbin/buffer 并让 B 继续提交。
11.3 故障范围
| 故障 | 默认隔离范围 | 动作 |
|---|---|---|
| 非法输入/shape/context | request | 提交前 400,不碰设备 |
| A admission/KV 满 | A worker | A 有界排队后 429/503,B 正常 |
| submit DeviceBusy | 对应 queue,待确认 | 有界 backoff,不立刻 reset |
| 输出数值不符 | model generation | 摘目标 alias;若共享配置不确定则摘 A/B |
| Wait timeout/CCU exception | session 或全板未知 | 停止新提交、drain、保存 trace,按厂商契约 reset |
| chip-scoped reset 失败 | device | 两个 alias 摘流,进程/设备恢复 |
| 板级 reset/驱动异常 | device | A/B 同时 503,重建全部 generation |
在故障注入证明之前,所有 Runtime timeout/crash 按“可能污染整板”处理。
12. 分阶段执行计划
Phase 0:冻结合同和基线
输入:当前 SDK、板卡、服务、模型和性能 harness。
工作:
- 固定 driver/firmware/CalRT/compiler/calbin/server 的版本与 hash。
- 获取 per-chip DRAM、SRAM、队列、reset topology 的书面口径。
- 建立
model_alias -> graph -> chip mask的版本化 manifest schema。 - 对现有单模型跑正确 TTFT/TPOT、功耗、温度、内存和 1h 稳定性基线。
出口:版本可重放,设备健康,旧模型行为可对比。当前可做,C0/C2。
Phase 1:单芯粒单模型
输入:厂商提供一个单芯粒小模型 calbin。
工作:
- 静态校验
SCHIP、mask、ELF、command、参数、KV、地址和 hash。 - chip0 configure;chip1 保持空闲。
- 跑 prefill/decode golden、1/16/512/4K prompt 边界和 32/128 token 输出。
- 用 trace 证明只在 chip0 执行,无跨芯粒 D2D。
- 重复 chip1 版本,验证对称性。
出口:两颗芯粒分别能独立完成同一模型的 prefill+decode。C3 -> 实测后 C0。
Phase 2:同一模型复制到两芯粒
输入:组合 calbin 或两个正式 chip session。
工作:
- A0 在 chip0、A1 在 chip1,tokenizer和权重版本相同。
- 先串行 golden,再同时提交不同 prompt。
- 交叉使用不同 sequence id、不同长度和不同取消时机。
- 比较两侧输出和 reference,检查 KV、buffer、job completion 无串扰。
- 用 trace/时间线证明执行重叠。
出口:先证明资源隔离,不把不同模型算子覆盖混入问题。
Phase 3:两个不同模型
输入:模型 A/B 均已单独通过 Phase 1。
工作:
- A/B 各自 tokenizer、sampling config、context、KV 和 buffer pool。
- 跑四种 prefill/decode 干扰组合。
- open-loop 分别压 A、B 和联合压测,验证各自 backpressure。
- A 过载、取消、非法请求时检查 B 的成功率和 P99。
- 分别验证 load/warmup/ready;组合 calbin 则验证整包生命周期。
出口:S1 服务并存与 S2 硬件并发都通过。
Phase 4:故障隔离
输入:厂商书面 reset/error 契约。
工作:
- 注入 A 的 invalid tensor、KV exhausted、submit busy、Wait timeout、CCU exception。
- 注入 A 的 drain deadline 和 unload/reload。
- 检查 B 是否继续生成、延迟是否在门槛内、KV 是否不变。
- 分别测试 session/chip/config/device reset。
- 若任何故障实际影响全板,将产品能力降级为“共享故障域”,并按 A/B 同时摘流设计。
出口:故障域有实测结论,不以 API 名称猜测隔离。
Phase 5:生产化和 24h soak
工作:
- 补齐 Router、鉴权、限流、metrics、trace、审计和 health。
- 混合流量 1h、8h、24h;记录功耗/温度/频率和内存回落。
- 100 次 load/unload;组合 calbin 跑 100 次整包切换。
- 灰度、自动降级、旧 generation 回滚和冷启动测试。
出口:满足第 13 节门槛后才对外声明双模型并发。
13. 验收矩阵和建议门槛
下面是第一版工程门槛;业务 SLA 更严格时取更严格值。
13.1 正确性
| 项目 | 通过条件 |
|---|---|
| 单模型 golden | 厂商定义的 logits tolerance/top-k/token 序列全部通过 |
| A/B 并发 | 至少 1000 请求无跨模型 token、logits、KV、sequence id 串扰 |
| tokenizer | alias 与 tokenizer/hash 一一匹配,错配启动失败 |
| context 边界 | 0、max、max+1 在提交前正确处理,无越界提交 |
| cancel | 取消只停止结果交付;在途 buffer 等待完成后回收 |
| generation | reload 后旧 completion 不得交付给新 generation |
13.2 并发和性能
定义:
T_serial = T(A isolated workload) + T(B isolated workload)
T_joint = wall time when A and B workloads run together
overlap_ratio = 1 - T_joint / T_serial
throughput_efficiency = joint_successful_tokens_per_s
/ (A_isolated_tokens_per_s + B_isolated_tokens_per_s)
建议通过条件:
- per-chip trace 显示 A/B compute 区间真实重叠。
overlap_ratio > 0且置信区间不跨 0;不能只看一次运行。- 稳态
throughput_efficiency >= 0.80。 - 联合负载下每个模型 P99 TTFT/TPOT 相比各自隔离基线退化不超过 25%。
- A 满载时 B 的成功率不低于 99.9%,除非触发明确的板级热/电/故障保护。
若吞吐未达门槛,先区分 compiler、共享 DMA、全局 Runtime 锁、功耗降频和服务调度,不能直接归因于芯粒算力。
13.3 生命周期和稳定性
- 100 次 load/unload 或整包 reconfigure 无泄漏、无
AlreadyConfigured、无旧 generation 完成。 - 24h 混合 soak 无 crash、hang、错误 token 或未知状态。
- 空闲恢复后设备内存回到基线;稳定漂移小于
max(64 MiB, 1% local DRAM)。 - pending/finished/left job counters 最终闭合。
- timeout 和 reset 后必须重新 golden+warmup 才能 Ready。
- A 队列/KV 满不使 B 的 queue depth、admission 或错误率同步恶化。
13.4 故障隔离等级
| 等级 | 能力 |
|---|---|
| F0 | 任一 Runtime 故障都需要整板恢复;A/B 同时摘流 |
| F1 | 请求和资源耗尽隔离,但 CCU/timeout/reset 共享故障域 |
| F2 | session/chip reset、unload 和 KV 污染均不影响另一模型 |
组合 calbin 很可能先达到 F1;只有逐项故障注入通过后才能声明 F2。
14. 指标和日志
每条请求:
request_id, model_alias, model_generation, chip_id(internal only),
prompt_tokens, output_tokens, queue_ms, ttft_ms, tpot_ms, e2e_ms,
finish_reason, error_class
每个 job:
job_id, session_id, model_generation, graph(prefill|decode),
engine(auto|ping|pong), submit_ns, done_ns,
h2d_ms, infer_ms, d2h_ms, output_status,
kv_before, kv_after, reset_generation
每颗芯粒:
memory_used/total, queue_depth, in_flight, completed, failed,
temperature, power, frequency, throttle, reset_count
取不到的 per-chip 指标写 null 并列入厂商缺口,不能用 0 或整板值冒充。request_id/job_id 进入 log/trace,不作为 Prometheus 高基数 label。
15. 厂商必须交付的 P0 清单
15.1 compiler/calbin
- 一个 1B-3B dense 模型的 chip0 单芯粒 calbin,含 prefill/decode、参数、KV、workspace、golden 和 tokenizer。
- 同一模型的 chip1 对称版本。
- 同模型双副本组合 calbin,用于先验证隔离。
- 两个不同模型 A/B 的组合 calbin。
- 每个包的 ONNX/hash、量化配置、compiler 容器/commit、完整命令和 compatibility matrix。
- 正式 manifest schema,明确
SCHIP/tarChipN/chipMask/calc_mchip语义。 - 静态/峰值内存报告,分别列权重、KV、workspace、code、command、I/O 和 guard。
- 证明单芯粒图无跨芯粒 D2D/gather/reduce 的 compiler report。
15.2 Runtime/driver
CalbinModel的 chip target 如何驱动 configure 和 SubmitJob。- 一个 calbin 内两个 SCHIP 模型能否同时执行,以及官方最小 runner。
- per-chip queue、job id、completion、DMA 和 sync unit 是否隔离。
- 两线程同时向不同芯粒 Submit/Wait 的线程安全保证。
- per-chip DRAM/SRAM 的真实容量、预留和查询 API。
- KV manager 的 model/session namespace 和失败回滚语义。
- session/chip/config/device reset 的精确影响范围。
- 独立 load/unload 是否支持;若不支持,组合 calbin 整包生命周期的正式流程。
- per-chip trace/counter/temperature/power/frequency API 和时钟域。
- Runtime/driver/firmware/compiler/calbin 的版本兼容矩阵。
15.3 验收材料
- 单模型孤立和双模型联合性能原始数据。
- A/B 并发时间线,能看到 chip0/chip1 overlap。
- 错误注入与 reset 影响范围报告。
- 24h soak 报告和内存回落数据。
- 已知限制:最大并发、context、batch、不可抢占、共享功耗/带宽和恢复限制。
只给两个单芯粒目录、没有组合 runner 和 reset/queue 契约,不足以开始产品接入。
16. 我方代码拆分
厂商 P0 产物到位后建议按以下 PR 推进:
| PR | 内容 | 依赖 | 退出条件 |
|---|---|---|---|
| 1 | exact model manifest 和静态验证 | 单芯粒/组合样包 | 坏 mask/hash/shape 启动前拒绝 |
| 2 | DeviceSupervisor + generation 生命周期 | Runtime 契约 | 单 configure、可 drain/recover |
| 3 | ModelWorker 抽象,拆 tokenizer/KV/buffer/queue | PR2 | A/B 可独立单测 |
| 4 | Router 按 alias 路由和 per-model admission | PR3 | A 过载不阻塞 B |
| 5 | JobContext、独占 buffer、异步 completion | PR3 | in-flight=1 无串扰 |
| 6 | 组合 calbin 双 worker 提交 | 厂商 runner | 同模型双副本并发通过 |
| 7 | 不同模型 A/B 和 metrics/trace | PR6 | S1/S2 验收通过 |
| 8 | 故障注入、灰度、回滚、24h soak | reset 契约 | F0/F1/F2 等级落定 |
在厂商产物和 API 到位后,我方纯软件首个 PoC 约需 5-8 个工程日,完整故障/性能/24h 验收再需 5-7 个工程日。厂商 compiler/Runtime 新能力的交付周期目前无法从本地资料可靠估算,应让对方分别给组合 calbin 和 chip session 两条排期。
17. Go/No-Go 决策门
| 决策点 | Go 条件 | No-Go/降级动作 |
|---|---|---|
| G0 单芯粒容量 | 静态预算和 load 峰值均留足 guard | 缩模型/context/batch,禁止硬塞 |
| G1 单芯粒正确性 | chip0/chip1 分别 golden 通过 | 回到 compiler/kernel 调试 |
| G2 双副本隔离 | A0/A1 无 buffer/KV/job 串扰 | 不进入不同模型阶段 |
| G3 S2 并发 | trace overlap + 联合性能达标 | 只声明 S1,调查全局锁/共享瓶颈 |
| G4 故障域 | 至少 F1,恢复流程确定 | 两 alias 绑定同一健康状态并同时摘流 |
| G5 生产 | 24h、100 次生命周期、回滚通过 | 保留单模型或两板方案 |
18. 最终建议
- 把需求名称改为“双芯粒双模型独立服务”,不要写“每个 CalCore 一个模型”。
- 立即向厂商索取“同一小模型双副本组合 calbin + 官方并发 runner”;这是最少变量、最快验证硬件隔离的路径。
- 第一轮用 1B-3B dense、B1、4K context;先 chip0/chip1 单独跑,再双副本,最后不同模型。
- 产品架构从一开始按两个 ModelWorker 设计,但板卡只由一个 DeviceSupervisor 管理。
- 若厂商确认 0.7.6 组合 calbin 能独立 dispatch,则先以它达到 S2/F1;独立 load/unload 和 F2 继续要求 chip session API。
- 如果厂商不能提供组合 calbin或芯粒级 queue/dispatch,当前单板方案 No-Go;业务上改用两张板/两台主机,每板一个模型。
19. 证据入口
- 研究导航
- 模型产物与内存映射
- 多芯粒、多模型、动态加载与并发演进设计
- Batch、KV 与多模型调度实现规格
- Runtime API 精确契约与错误恢复手册
- calbin 产物字段字典与静态验收规范
- 新模型适配工程 Runbook
- 性能压测、故障注入与验收规范
本文只使用已下载到本地的 SDK、产物、源码和环境记录;本轮没有连接或修改远端测试样机。
20. 完整 Git 历史带来的新增约束
完整 CalRT/llama.cpp 历史没有证明当前 Runtime 已有芯粒级独立 session,但补强了三个实施判断:
- CalRT 历史中存在 async/serial、job engine thread-safe、重配其他 calbin、多芯片 configure 和 reset 演进,因此路径 A/B 具备可研究的底层基础,不是从零开始。
- 同一历史也记录过 async 空指针、多线程同步、job id 和 release/reset 修复,因此所有并发能力必须在独立 runner 中按 commit 风险回归,不能仅靠 API 签名判定可用。
- 当前可读驱动源码是 0.9.0,而运行模块是 1.0.0;在取得上游 driver mirror 和 reset/DMA/lease 差异前,不能把改驱动当作短期生产路径。
llama.cpp 1bec0db 的“2 calbins”只是 benchmark 顺序加载;origin/runtime_replace 只有一个 EngineCore 和一个 calbin_path;两者均不构成 S1/S2 或 F1/F2 证据。三个过期 stash和 calrt: add slice 不可达实验已经恢复,可用于重建 buffer slice、计时和版本输出实验,但必须在当前基线上重新验收。
新增 P0 厂商交付项:驱动 1.0.0 的完整 Git mirror、0.9.0->1.0.0 变更、PR/Issue/CI、Runtime/firmware 兼容矩阵,以及 cal-llm SDK/ABI。详见《源码、Git 历史与驱动证据分析》。