402 lines
19 KiB
Markdown
402 lines
19 KiB
Markdown
# 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 转为 FP32,CPU 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. 目标架构
|
||
|
||
```mermaid
|
||
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"]
|
||
```
|
||
|
||
核心对象:
|
||
|
||
```text
|
||
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 至少包含:
|
||
|
||
```text
|
||
(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 状态机
|
||
|
||
```mermaid
|
||
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 B:ping/pong,in-flight=2(C1/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=1`,KV 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 更新拆成三阶段:
|
||
|
||
```text
|
||
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 模型 A,chip1 模型 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 模型状态机
|
||
|
||
```mermaid
|
||
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 parallel(C0)
|
||
|
||
两芯粒均执行 48 层 attention、MoE、norm 和 residual;embedding 只在 chip0。每层有 gather/reduce/sync,prefill 还有 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/B:TP2、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 rollback,buffer 归还或隔离 | 连续失败进入 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 历史与驱动证据分析》。
|