Files
calculet-npu-research-archive/reports/Calculet-NPU-多芯粒多模型动态加载与并发演进设计-20260802.md

19 KiB
Raw Permalink Blame History

Calculet NPU 多芯粒、多模型、动态加载与并发演进设计

日期:2026-08-02
基线:CalRT 0.7.6、llama.cpp fd9bd632、单物理 VirtualDevice、双芯粒 Qwen3 calbin

实施下钻:执行器类、锁和 buffer/KV 所有权见《Calculet NPU 异步执行器与生产稳定性实施规格》;B4/B8/B16 lane、continuous batching、KV 状态、公平队列和 generation load/unload 见《Calculet NPU Batch、KV 与多模型调度实现规格》。本文保留演进决策和能力边界。

1. 结论

当前可安全实现的第一步不是“同时跑很多模型”,而是把单模型的同步请求路径改造成有明确所有权、可观测、可取消、最多两个 in-flight job 的异步执行器。完成后再引入厂商编译的 batch 4/8/16 decode calbin,才有真正的硬件 continuous batching。

能力边界:

目标 当前状态 所需工作 状态
单请求单模型双芯粒 已运行 稳定性修复 C0
同一模型 ping/pong 两任务在途 SDK 有基础,产品未用 buffer/job/KV 重构和验证 C1/C2
同模型 batch 4/8/16 当前 calbin batch 1 厂商新 calbin + scheduler C3
单芯粒运行小模型 当前产物固定双芯粒 厂商单芯粒 calbin C3
两芯粒各跑一个小模型 无相应产物/隔离契约 单芯粒 calbin + chip affinity/隔离 API C3
同板多个模型常驻 0.7.6 未证明可同时 configure 多 calbin 内存规划、命名空间、厂商确认 C3
动态 load/unload 可设计服务生命周期 Runtime 释放语义和恢复验证 C2/C3
多物理 NPU 板 0.7.6 明确只支持一个物理设备 新 Runtime/多设备管理层 C4(0.7.6)/C3
expert parallel / pipeline parallel 当前不是这两类并行 compiler partition + Runtime 调度 C3
cal-llm EngineCore 路径 仅 Git 历史分支 正式 SDK、ABI、产物和迁移测试 C3

2. 当前执行模型为什么不能直接并发

当前服务可以有多个 llama.cpp slot,但 NPU adapter 最终仍串行:

  1. batch 先按唯一 sequence 拆成多个 ubatch。
  2. 根据第一个 ubatch 的 token 数选 prefill 或 decode;硬件不允许同次提交混合两者。
  3. 创建一对 InputBuf/OutputBuf,然后循环各 sequence。
  4. 每次调用 infer() 后立即 Wait()
  5. prefill/decode 各自映射到 calrt_context 中的一组长期 vector。
  6. 输出完成后将整 vocab BF16 logits 转为 FP32CPU sampling。
  7. CSR setter、infer()Wait() 的错误码均未检查。

因此有四个硬限制:

  • 当前 calbin max_batch_size=1,服务聚合并不等于 NPU batch。
  • 同一 prefill/decode backing vector 会被后续任务覆盖。
  • KV Apply 与提交/完成没有事务边界,失败时可能无限循环或 abort()
  • 立即 Wait 使 Runtime 的非阻塞、ping/pong 和 parallel mode 没有形成 pipeline。

只把 Wait() 移走会产生 use-after-reuse、输出串扰和 KV 状态提前提交,不是有效优化。

3. 目标架构

flowchart LR
    API["API/slot scheduler"] --> ADM["Admission + KV allocator"]
    ADM --> BATCH["Prefill/decode batch builder"]
    BATCH --> SUB["Per-device submitter"]
    SUB --> Q0["JobContext ping"]
    SUB --> Q1["JobContext pong"]
    Q0 --> NPU["CalRT / NPU"]
    Q1 --> NPU
    NPU --> CMP["Completion worker"]
    CMP --> KV["KV commit/rollback"]
    CMP --> POST["logits postprocess/sampling"]
    POST --> API
    CMP --> OBS["metrics + trace + health"]

核心对象:

DeviceExecutor
  device_id / vdevice
  lifecycle_state
  configured_calbin
  submit_queue
  completion_worker
  buffer_pool[model, task_type, engine_slot]
  kv_allocator
  health/recovery controller

JobContext
  job_id / request_ids / sequence_ids
  model_generation
  task_type(prefill|decode)
  engine_slot(auto|ping|pong)
  exclusive input/output buffers
  exclusive host backing storage
  tensor slices and CSR snapshot
  KV reservation transaction
  submit/deadline/cancel state
  completion status and timings

每个 JobContext 从构造到 completion 期间独占所有会被 DMA 访问的 host memory。只有 OutputBuf::Wait() 返回并完成 D2H 后才能归还池。取消不能立即释放 buffer;它只标记“不再交付结果”,仍要等待 Runtime 完成或执行经过验证的 reset。

4. Buffer 所有权模型

4.1 池化单位

池 key 至少包含:

(calbin_generation, exact_submodel_name, task_type, engine_slot, shape_profile)

一个池项包含 InputBuf、OutputBuf、token/position/embedding/logits backing memory、slice 初始状态和固定 tensor pointers。不能只池化 Runtime 对象而继续共享 std::vector

4.2 状态机

stateDiagram-v2
    [*] --> Free
    Free --> Preparing: acquire
    Preparing --> Submitted: infer succeeds
    Preparing --> Free: validation fails
    Submitted --> Running: output status
    Submitted --> Completing: done fast path
    Running --> Completing: done/CCU exception
    Completing --> Free: D2H + postprocess + reset
    Submitted --> Quarantined: timeout/reset
    Running --> Quarantined: timeout/reset
    Quarantined --> Free: device generation changed and buffers rebuilt

归还前必须:UndoSlice/ResetAllTensors、清理 CSR、确认无 DMA、清理 request 引用和敏感数据。CCU exception、timeout 或 device reset 后的 buffer 不直接复用,而是绑定新 device/calbin generation 重建。

4.3 零拷贝边界

MapBuf 可以避免 adapter 内额外复制,但不代表 host memory 可以任意移动。backing storage 在任务完成前必须地址稳定。std::vector::resize、模型切换或 pool 扩容不得作用于 in-flight 项。

5. 异步提交与完成

5.1 Phase A:保持 in-flight=1,先解耦线程(C2

先不追求性能:

  1. 请求线程构建 JobContext 后入 submit queue。
  2. device submitter 调用 infer() 并检查返回码。
  3. completion worker 调用 Wait()
  4. 结果通过 future/callback 回到 slot scheduler。
  5. 队列、超时、取消、错误和 buffer 生命周期有单测。

这一步验证异步架构本身,不改变 NPU 执行顺序。

5.2 Phase Bping/pongin-flight=2C1/C2

在隔离 runner 中先验证:

  • EnableParallelMode(true) 的准确语义。
  • infer_with_fixed_task_type(...PING/PONG)SubmitJob(forceEngineMode)
  • ping/pong 是否只表示命令/buffer bank,是否能并行 H2D、compute、D2H。
  • OutputBuf status 的线程安全和转换顺序。
  • pending/finished/left job counter 的含义。
  • 两个 task 使用不同 buffer、不同 sequence 时的输出和 KV 一致性。

产品中 in-flight 上限必须来自经验证的 Runtime 能力,不由请求并发直接决定。初始固定为 2;发现 counter、状态或正确性异常时自动降级为 1。

5.3 Phase C:多队列调度(C2

建议三类队列:

队列 优先级 策略
decode-ready 延迟敏感,按 deadline/轮转
short prefill 长度分桶,限制连续占用
long prefill 低但防饥饿 token budget,定期强制服务

若 Runtime/图不能抢占,长 prefill 会阻塞 decode。调度器应限制单次 prefill token chunk,但只有 calbin 支持相应长度/增量语义时才能切分;不能擅自把一个整 prompt 切成多个 prefill 而假设 KV 等价。

6. Batch 4/8/16 与 continuous batching

6.1 为什么必须有新 calbin

当前子模型的 max_batch_size=1KV layout 也是相应配置。llama.cpp 把多个 slot 放入软件 batch 后仍逐 sequence 调用 NPU,不会自动变成硬件 batch。需要厂商至少输出 decode B4/B8/B16,最好同时提供 prefill 的可行 batch/shape 组合。

6.2 调度模型

每个 decode tick

  1. 从 decode-ready 选择最多 B 个 sequence。
  2. 为每个 sequence 分配稳定 hardware batch lane/KV slot。
  3. [B,1] token/position 和每 lane sequence length。
  4. inactive lane 按厂商定义 mask,不能伪造 token。
  5. 提交一次 decode,返回 [B,1,vocab] 或等价 layout。
  6. 按 lane 完成 sampling,将仍活跃的请求放回 ready queue。

6.3 选择 B4、B8、B16

不能假设 batch 越大越快。分别测:

  • 单请求 TPOT 及 P99。
  • 满 batch aggregate tokens/s。
  • 不满 batch 的有效率。
  • 长短 sequence 混合时的 padding/mask 成本。
  • KV DRAM、workspace 和每 token D2H。
  • 热/功耗稳定状态。

Runtime KV 头文件提到 batch 16 layout 和 ONLY_VALID/ALL/AUTO,但只是能力线索;需要对应 calbin、明确 inactive-lane 规则和数值测试后才是产品能力。

7. KV 事务与 admission control

将 KV 更新拆成三阶段:

reserve(sequence, target_length) -> submit(job) -> commit(job)
                                      | error/cancel
                                      v
                                  rollback(job)

规则:

  • admission 先调用 canAllocate 或等价容量模型;不足时排队/拒绝,不忙等。
  • reserve 记录旧长度、目标长度、hardware batch 和 generation。
  • submit 失败直接 rollback。
  • Runtime 完成且输出有效才 commit 新长度。
  • CCU exception/timeout 后若 KV 一致性未知,隔离相关 sequence;必要时 reset configuration 并清空整个 generation。
  • context shift、seq copy/keep/add/div 在 CalRT adapter 完整实现并通过 reference 前禁用。

当前 Apply() 失败前循环无 sleep/timeout,完成后失败则 abort(),必须在任何并发扩展前修复。

8. 单芯粒小模型

8.1 需要的厂商产物(C3

单芯粒运行不是在当前双芯粒 calbin 上传 chipId=0。必须重新编译:

  • num_chips=1、chip mask、参数文件和 reservation。
  • 单芯粒 kernel/layout、无跨芯粒 gather/reduce/D2D。
  • batch/context profiles。
  • 单芯粒 golden 和性能报告。

8.2 可能的部署形态

形态 价值 依赖
chip0 单模型,chip1 空闲 功耗/容量验证 单芯粒 calbin、power/affinity
chip0 模型 Achip1 模型 B 两租户/两模型并行 独立地址空间、命令队列、reset domain
双芯粒大模型与单芯粒小模型切换 峰谷调度 可靠 unload/load、内存重配置

0.7.6 虽有 per-chip memory access,不能据此推断 configure 和 job engine 支持两个独立 chip-scoped VirtualDevice。必须向厂商取得 chip affinity、隔离和 reset 语义。

9. 多模型常驻

9.1 当前障碍

参考 Qwen3 参数 16.938 GiB、BF16 KV 3.750 GiB,总 DRAM reservation 推断为 28.150 GiB/双芯粒,已经占用大部分板上计划空间。CalrtDevice 暴露“current calbin”,现有代码也只有一个 calrt_context/calbin。没有证据表明 0.7.6 能同时 configure 多套独立 calbin 并隔离地址。

9.2 需要的 Runtime 契约(C3

  • 多 calbin 的 model namespace 和地址重定位。
  • 每 calbin 参数/KV/workspace reservation 与 quota。
  • job 中显式 model handle,不依赖全局 current calbin。
  • load/configure 与在途任务的原子性。
  • 单模型 unload 是否影响其他模型。
  • chip/core affinity、优先级、公平性和 reset domain。
  • 参数共享、prefix/KV 共享是否允许及其生命周期。

9.3 如果 0.7.6 只能单 calbin

仍有两个可行方案:

  1. 让厂商把多个子模型打进同一个组合 calbin,前提是地址和命令由编译器统一规划。
  2. 进程级单模型,使用受控 unload/configure/load 切换;切换期间 drain,不追求同时常驻。

不能通过手工拼接两个模型目录或改子模型名称实现组合 calbin。

10. 动态 load/unload 生命周期

10.1 模型状态机

stateDiagram-v2
    [*] --> Unloaded
    Unloaded --> Validating: load request
    Validating --> Configuring: manifest/hash/compat pass
    Validating --> Failed: reject
    Configuring --> Warming: configure succeeds
    Configuring --> Failed: configure/reset fails
    Warming --> Ready: golden + warmup pass
    Ready --> Draining: unload/replace/fault
    Draining --> Unloading: in-flight=0
    Unloading --> Unloaded: release + memory verified
    Failed --> Unloaded: cleanup verified

10.2 load

  1. 在非设备线程验证 manifest、hash、文件权限、版本和容量。
  2. 进入设备写锁,停止 admission。
  3. 若替换模型,先 drain 当前 generation。
  4. CreateCalbin、configure、创建 buffer pool/KV manager。
  5. 运行 golden prefill/decode 和固定 warmup。
  6. 原子发布新 model generation,恢复 admission。

10.3 unload

  1. model alias 从 Ready 变为 Draining,新请求拒绝或转移。
  2. 等待/取消业务,但始终等待底层 DMA 完成。
  3. 释放 KV、buffer pool、Calbin/context。
  4. 按厂商契约 ResetConfiguration/ClearDynamicMem;不默认全设备 reset。
  5. 检查 Runtime 内存用量回落和 device health。
  6. 只有清理确认后进入 Unloaded。

所有 job 携带 generation。旧 completion 到达时不得写入新模型的 slot/KV/buffer。

11. 双芯粒并行演进

11.1 当前 tensor parallelC0

两芯粒均执行 48 层 attention、MoE、norm 和 residualembedding 只在 chip0。每层有 gather/reduce/syncprefill 还有 dynamic D2D。它适合单个大模型,但跨芯粒通信会随层数重复。

11.2 expert parallel 候选(C3

按专家把权重分到芯粒,router 后对 token 做 all-to-all。潜在收益是降低每芯粒专家权重和 grouped GEMM 工作;风险是热点专家、少 token 时利用率低、通信和动态 dispatch。必须测每层 expert histogram、跨芯粒 token bytes、all-to-all 时间和负载不均。

当前两芯粒都含 128 experts 的权重分片,不是 EP。EP 需要 compiler 重新 partition 和生成通信命令。

11.3 pipeline parallel 候选(C3

例如 chip0 负责 0-23 层、chip1 负责 24-47 层。参数只跨边界传 activation,通信频率较低;但单请求两个阶段串行、microbatch 小会产生气泡,KV 也按层分布。只有 batch/并发足够时可能优于逐层 TP。

11.4 混合方案

候选必须由编译器产出不同 calbin 做 A/BTP2、PP2、EP2、TP+EP。llama.cpp 只负责请求和 batch 调度,不能在 Host 侧重排当前静态命令流来改变 partition。

12. 多物理板

CalRT 0.7.6 的 VirtualDevice 头文件明确注明临时只支持一个物理设备,内部仅有一个 shared_ptr<CalrtDevice>。因此:

  • 一个 VirtualDevice 不是多板资源池。
  • GetDeviceInfo(idx)idx 参数不构成多板证据。
  • per-chip ReadMem/WriteMem 是板内芯粒访问,不是板间通信。
  • 当前双芯粒 calbin 不能跨两张 PCIe 卡部署。

短期横向扩展只能采用“一进程/一板/一模型实例 + 上层负载均衡”,前提是 Runtime/driver 能以独立进程稳定绑定各板;本地资料尚不能确认设备选择 API,需要厂商支持。统一多板 continuous batching、KV 迁移和模型切分属于新 Runtime 设计。

13. origin/runtime_replace 与 cal-llm

Git 历史中 origin/runtime_replace 包含 cal-llm EngineCore、profile、capacity 和错误处理方向。这说明厂商正在探索用更高层推理 Runtime 替换当前直接 CalRT adapter,但它不是当前生产:

  • 当前 HEAD/origin/dev 是 fd9bd632
  • 当前镜像链接 CalRT 0.7.6 并使用 llama-calrt.cpp
  • 分支代码不等于可发布库、头文件、ABI、calbin 或支持承诺。

评估 cal-llm 前要求:SDK/header/library、版本与 ABI、支持模型/产物格式、batch/KV/sampling/取消/错误契约、性能 trace、迁移指南和至少一套可运行镜像。它可作为长期替换候选,不能写入当前 C0 能力。

14. 故障恢复

故障 首选处理 升级处理
admission/KV 不足 排队、429/503、有界超时 不 reset
submit 返回错误 job rollbackbuffer 归还或隔离 连续失败进入 drain
output CCU exception 相关 job 失败,KV 标记未知 ResetCCU/Configuration,重新 warmup
Wait 超时 停止新提交,保留 buffer 厂商定义的 cancel;否则 reset generation
output 数值异常 隔离 calbin generation golden 复测,回滚旧模型
device unhealthy 全实例 drain device reset/进程重启/摘流

reset 不是普通错误的第一反应。所有 reset 必须记录原因、在途任务、前后版本/health 和恢复结果,并限制重试次数,避免 reset storm。

15. 指标与验收

必须增加:

  • submit queue 深度、pending/finished/left jobs、in-flight。
  • buffer pool 使用/等待、KV slot 使用/等待、admission reject。
  • 每任务 queue/H2D/infer/D2H/postprocess/sampling。
  • prefill/decode 分桶 P50/P90/P99。
  • batch fill ratio、每 tick active lanes、tokens/s。
  • ping/pong 利用率和 overlap 率。
  • per-model/chip memory、D2D bytes/time、CCU exception、reset。
  • generation、calbin hash、Runtime/driver/firmware 版本标签。

验收顺序:正确性 -> 资源生命周期 -> 错误恢复 -> 并发 -> 性能 -> 24h 稳定性。吞吐达标但请求串扰、KV 不一致或 reset 后不稳定时视为失败。

16. 建议实施里程碑

里程碑 工作 完成条件
M0 稳定基线 修 KV 忙等/abort、manifest、metrics guard、计时 现有 batch1 24h 稳定
M1 异步骨架 JobContext、buffer pool、submit/completion in-flight=1 行为等价
M2 ping/pong parallel/fixed task 隔离验证 in-flight=2 无串扰,收益可测
M3 batch decode 厂商 B4/B8/B16 + scheduler/KV lanes continuous batching 正确且 P99 可控
M4 单芯粒小模型 dense 单芯粒产物 与双芯粒模型可切换,资源可回收
M5 多模型 组合 calbin 或 Runtime 多模型契约 load/unload soak 无泄漏/串扰
M6 新并行 TP/EP/PP A/B 用实测而非架构推断选择
M7 多板 新 Runtime 或多进程绑定 明确设备选择、隔离和恢复

前三个里程碑不应并行跳跃:M0 不稳定会污染 M1/M2 的所有结果,M1 的所有权模型又是 M2/M3 的前置条件。

17. Git 历史对演进顺序的校正

完整 CalRT 历史确认了 async/serial mode、thread-safe job engine、multi-chip configure、ResetConfiguration 和连续同模型 fast path 的演进;它们分别对应 M1/M2/M5 的候选实现基础。但历史同时出现 async nullptr、多线程同步、job id、设备状态、内存释放和 CCU reset 修复,说明并发/切换功能的回归面必须覆盖:

  • 两线程 Submit/Wait 与 ping/pong 交错。
  • timeout、cancel、CCU exception 后 buffer/KV/generation 隔离。
  • 同模型连续运行与 A/B 模型切换两条状态路径。
  • ResetConfiguration、Release、soft reset 的真实作用域。
  • queue counters、jobs left 和 device owner 在多线程/多进程下的一致性。

llama.cpp 历史还明确区分了三类证据:dev 主线是当前生产基线;origin/runtime_replace 是未合入的 cal-llm 集成方向;当前/过期 stash 和不可达 commit 是实验。实现时只允许主线作为基线,其他内容按独立 patch 重建,不直接把历史分支合并到产品。

M5 增加硬门:取得驱动 1.0.0 对应 Git commit 和 Runtime/firmware 兼容矩阵。现有 0.9.0 源码可以做设计分析,不能替换运行模块。完整资产、恢复 refs 和历史风险清单见《源码、Git 历史与驱动证据分析》。