sskj/docs/Kimi-K3_PP与DFlash迁移审计.md

120 lines
10 KiB
Markdown
Raw 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.

# Kimi-K3从 PP + DSpark 迁移到 PP + DFlash
更新2026-08-31PP、传输与 MoE 集成首版。
## 1. 决策与范围
**优先推进 DFlash 的 PP + PD 适配。** #33863 的分段投影设计能复用于 DFlash但需要补齐 DFlash worker、调度入口和异构 TP 下的 draft KV 传输。
当前已实现 DFlash PP worker、Kimi 跨 stage capture、PD 输入衔接、异构 TP 的 draft KV 传输,以及 Kimi FlashInfer 布局/SiTU 接入。首轮 P 启动暴露普通预热误带 verify metadata 的问题已修复并增加回归23 项 CPU 测试通过。SM120 编译及 Kimi SiTU GPU 集成回归已通过。修复镜像已同步全部八节点Run `pd-dflash-pwarm1-20260831-1652` 正在重新验证服务启动;尚无 PD 请求和 GSM8K 结果。详见 [实现进度与命令](evidence/kimi_k3_pd_dflash/README.md)。
暂停 EAGLE3 baseline。KV cache 保持 BF16chunk 保持 8192。FlashInfer 使用官方 0.6.18 加已合并 #4460 的显式 backport原版 0.6.18 wheel 尚未包含所需 CUTLASS SiTU 接口。
目标拓扑P 使用 601604 的 TP4/PP8/EP4 与已适配的 FlashInfer MXFP4D 使用 605608 的 TP32/PP1接入 DFlash。这里适配的是 **P 侧 PP 生成 draft 上下文**D 侧投机执行仍为 PP1。
审计固定版本:
| 对象 | 版本 |
|---|---|
| SGLang main | `3139ceaeec50868a441d68dc231663f3777e0d93` |
| PR #33863 head | `6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5` |
| PR 状态 | Open维护者要求拆分 DSv4 PP/PD、DSpark、Kimi/线性注意力工作 |
PR 公布的准确性数据来自 DSv4 Flash / H20不能代替本次 Kimi-K3 / 6000D / P-TP4 与 D-TP32 验证。[PR #33863](https://github.com/sgl-project/sglang/pull/33863)
## 2. 为什么可以迁移
DSparkDraftModel 继承 DFlashDraftModel。普通 Kimi draft 的 prompt 上下文处理都包含:采集多个 target 层的 hidden拼接后线性投影再归一化生成 draft KV。
#33863 将这一步改为:
```text
各 P/PP stage采集自己负责的 hidden → 使用对应权重列做局部投影
PP stage 间:传递并累加投影结果 [token 数, 7168]
最后一个 P/PP stage统一 RMSNorm → 各 draft 层 K/V 投影、K norm、RoPE → 写 draft KV
P → D传 target KV、KDA 状态及 draft KV
D使用已有 DFlash proposer / verify / commit 流程
```
依据是线性运算 `concat(h_i) × W^T = sum(h_i × W_i^T)`。归一化必须在求和后做。BF16 分段累加会改变舍入顺序因此需要比较中间张量、logits 和 token 接受行为。
关键接口已经加在共用 DFlash 模型中:[project_target_hidden_partial](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/models/dflash.py#L683)。最后阶段的归一化及 KV 写入实现位于 [write_projected_context_kv](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/models/dspark.py#L751)。
这一路线保留 P 侧 PP8 的收益,不要求把各层原始 hidden 通过 PD 网络传给 D。
## 3. 需要补哪些代码
以下路径均相对于 SGLang 的 `python/sglang/srt/`
| 模块 | 已有基础 | 本次增量 |
|---|---|---|
| `models/kimi_k3.py` | PR 已支持各 PP stage 局部采集 DSpark 特征 | 接通 DFlash capture setter按 DFlash checkpoint 的层号及 Kimi residual 语义采集 |
| `models/dflash.py` | PR 已有局部线性投影 | 复用投影;最后阶段归一化后复用 DFlash 的逐层 KV 写入路径 |
| `speculative/dflash_worker_v2.py` | 非 PP prefill 的 hidden → draft KV | 接收、转发 PP proxy仅最后阶段写完整 draft KV处理非末 stage 没有 logits/next token 的情况 |
| `managers/scheduler_pp_mixin.py` | PR 的成功交集、失败并集及一致释放机制 | 将目前 DSpark 专用的 proxy/input 衔接扩展到 DFlash保持请求顺序和完成条件一致 |
| `speculative/spec_info.py` | 当前 PD 输入分派只有 EAGLE、DSpark | 接入 DFlash 输入构造,并验证 overlap FutureMap 与 idle batch 生命周期 |
| `disaggregation/prefill.py``utils.py``mooncake/conn.py` | 最后 PP stage 传 draft KV、按层号配对 | 加入 draft GQA 的 head 分片/复制与收发端独立 stride保留 target MLA/KDA 的已有路径 |
| `arg_groups/speculative_hook.py``validation_hook.py` | 当前 main 拒绝 PP + DFlash | 仅放开已实现的 PD-prefill + PP 组合D 端 PP1 范围不变 |
关键证据:
- [Kimi 局部层采集](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/models/kimi_k3.py#L2912)。
- [DSpark PP 上下文处理](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/speculative/dspark_components/dspark_worker_v2.py#L655)。
- [PP scheduler 的 DSpark 专用入口](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/managers/scheduler_pp_mixin.py#L1391)。
- [main 的 PD 输入分派](https://github.com/sgl-project/sglang/blob/3139ceaeec50868a441d68dc231663f3777e0d93/python/sglang/srt/speculative/spec_info.py#L174)DFlash 仍返回 None。仓库里的 `dflash_disaggregation.py` 辅助函数尚未被这个入口调用,不能仅凭文件存在认定 PD 已支持。
相关 [#36140](https://github.com/sgl-project/sglang/issues/36140) 记录了输入缺失和 draft 状态缺失问题;[#36277](https://github.com/sgl-project/sglang/pull/36277) 是提前拒绝不支持配置的保护性改动,不是 DFlash PD 的实现。
## 4. 异构 TP 的明确风险
605 的 `/data/hf_models/Kimi-K3-DFlash/config.json` 为 6 层 draft、8 个 KV head、head_dim=128、BF16、4096 sliding windowtarget capture 层是 `[19,37,54,66,78,90]`
[DFlashAttention](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/models/dflash.py#L142) 使用 `max(1, total_kv_heads // tp_size)`
| draft 布局 | 每 rank KV heads | BF16、page=64 时单层 K 的页大小 |
|---|---:|---:|
| P TP4 | 2 | `64 × 2 × 128 × 2 = 32768 bytes` |
| D TP32 | 1按 head 复制到多个 rank | `64 × 1 × 128 × 2 = 16384 bytes` |
PR 当前 [Mooncake flat 传输分支](https://github.com/sgl-project/sglang/blob/6465a6f3d3b6c8b7fee40fba0fdc09cf5e9ca1c5/python/sglang/srt/disaggregation/mooncake/conn.py#L686) 按 layer ID 配对后,对源偏移、目标偏移和复制字节数都使用源端 `item_len`。Kimi hybrid MLA 会进入这条路径。层号正确只能确认“哪层到哪层”,没有解决“该层哪些 head 到哪个 rank”。
### CPU 验证结果
从该 PR 源码直接用 AST 提取 `_send_kvcache_generic``build_transfer_entry_pairs`,使用 synthetic 地址、单页索引和记录型传输函数执行。未调用 GPU、Mooncake 或 RDMA。
[地址规划结果与源文件 SHA256](evidence/kimi_k3_pp_dflash_pr33863_transfer_plan.json)。
| 用例 | 地址/长度是否符合目标布局 |
|---|---|
| 收发双方均为 1 KV head | 符合 |
| P 为 2 head、D 为 1 head | 不符合:目标偏移和复制长度均按源端大条目计算 |
具体用例D buffer 起始地址设为 2000000目标 page ID=2。正确目标地址为 `2000000 + 2×16384 = 2032768`,当前函数生成 `2065536`,复制 32768 bytes 而不是单 head 页的 16384 bytes。
这是特定布局的地址规划复现,尚未启动完整服务复现。迁移时必须加入每条目的布局校验,并实现 GQA head-aware 传输;不能直接复用 flat copy。还需确认 D 接收到全部 target/draft 组件后才进入首轮 draft。
## 5. 实现与验收顺序
1. **局部数学与协议验证。** 检查各 stage capture 层覆盖、feature 顺序、空 capture stage、单次 RMSNorm比较拼接投影与分段投影误差。测试 TP4→TP32 的全部 8 个 draft KV head、K/V、6 层、页索引以及失败清理。源/目标布局不兼容时在传输前报错。
2. **最小 PD 请求。** 保留 P TP4/PP8、D TP32/PP1先 C1。核对真实 buffer metadata、draft KV 到达情况、首轮 draft logits以及成功/失败后的资源释放。
3. **GSM8K 固定 64 题。** 相同 prompt/template、采样设置依次测试 C1/C8保留准确率、完成率、接受长度、接受直方图、Output TPS、总耗时与显存状态。当前客户端非流式不把总耗时换算成 TTFT/ITL。输入不裁剪输出上限 512报告截断不新增 16K synthetic 测试。历史 5-shot 与前 64 题有重叠,本轮沿用以便部署回归,不能作为独立无泄漏的模型准确率评估。
4. **验收后决定继续投入。** DFlash 正确性与部署通过且有实际 Decode 收益,继续 PD 调优;若关键适配无法通过,或接受行为同样异常,切换到下面的 D-only 诊断。
真实实验代码继续维护在 601 的 `/data/hzy/sskj` 工作区,不改同事部署目录,不再创建分支。本轮核实当前检出名为 `hzy-kimi3-pd-pp8-standard`,保持现状;独立 SGLang 源码位于 `/data/hzy/src/sglang-kimi-pp-dflash-33863`
## 6. DSpark 接受长度诊断的备用路径
DSpark 低接受长度的问题先独立于 PD 排查。PP + PD 适配本身不会自动修复 D-only 已存在的问题。
| 检查项 | 目的 |
|---|---|
| checkpoint、tokenizer、mask token、target_layer_ids、RoPE | 排除 draft/target 配置失配及错误 hidden 来源 |
| 相同 token 前缀下,逐位置 draft token 与 target logits | 找到拒绝从第几个位置开始,区分首 token 错位与持续预测质量问题 |
| verify 的 KDA/SSM 状态更新、回滚与 token 位置 | 判断是否首次可用、随后状态漂移 |
| fused/replay 路径与参考执行比较 | 将 kernel/状态管理错误与 drafter 本身质量分开 |
| 接受统计定义、实际 proposal 数、bonus token | 统一接受长度口径,防止指标解释错误 |
605 的 DSpark checkpoint 为 block=7、5 层、capture `[7,23,51,67,83]`DFlash 为 block=16、6 层、另一组 capture 与 RoPE。二者的原始平均接受长度不能单独判断实现异常需同时看每次实际提议数、逐位置接受比例和实际吞吐。
本轮不重跑 no-spec 性能基线,保留此前结果作为参考。当前尚未确定 DSpark 接受长度异常的根因。