Files
agentic-pd-hybrid/docs/archive/STRUCTURAL_VALIDATION_REPORT_ZH.md
kzlin 7590e55189 docs: archive deprecated docs to docs/archive/, drop E1 from onboarding
Two cleanups:

1. Drop "E1: naive 1P3D default" experiment from the onboarding manual.
   GPU hours are precious; naive 1P3D + policy=default has near-certain
   loss on multi-turn cache hit (it's round-robin without prefix awareness),
   so the comparison doesn't add information vs E1=naive 1P3D kv-aware.
   The new manifest has only 2 runs: E1 (naive 1P3D kv-aware) + E2 (KVC
   v2 + RDMA). Run-time budget drops from 16.5h serial to 11h serial /
   5.5h parallel. Updated:
   - §0 TL;DR ("3 组" -> "2 组")
   - §2 H1 hypothesis (drop "default and kv-aware each one" -> just kv-aware)
   - §3.1 experiment matrix (3 rows -> 2 rows + rationale for the drop)
   - §3.2 startup config (drop E1 default section, renumber E2/E3 -> E1/E2)
   - §6 decision table + expected-range table
   - §7 FAQ ("3 个 E1-E3" -> "2 个 E1-E2")
   - §9 deliverables

2. Move 8 deprecated docs to docs/archive/:
     AGENTIC_FIT_ANALYSIS_ZH.md         (ts=10 era analysis; superseded)
     STRUCTURAL_VALIDATION_REPORT_ZH.md (ts=10 era validation; superseded)
     KVC_DEBUG_JOURNEY_V1_TO_V5.md      (v1-v5 sweep process notes)
     V5_PROFILE_INVESTIGATION_ZH.md     (v5 1Hz polling investigation)
     REFACTOR_PLAN_ZH.md                (v0 plan; superseded by V1)
     KVCACHE_CENTRIC_PROGRESS_ZH.md     (earliest 2026-04-27 progress)
     SWEBENCH_EXPERIMENT_PROGRESS.md    (early SWE trace setup)
     SWEBENCH_EXPERIMENT_RESULTS.md     (early SWE result snapshot)

   All cross-references in active docs (V2_DEEP_ANALYSIS / V2_RESULTS /
   REFACTOR_PLAN_V1 / TEAM_REPORT / ONBOARDING) rewritten from
   `docs/FOO.md` to `docs/archive/FOO.md` via sed pass.

   Added `docs/archive/README.md` explaining what each archived doc is
   and when (if ever) to reopen it. Designed so a new reader hitting
   the archive dir immediately knows it's not required reading.

After this commit the active docs in docs/ are 9 files (down from 17),
which should make the onboarding doc's "Level 1 / Level 2 / Level 3"
classification self-evident.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:40:35 +08:00

15 KiB
Raw Blame History

结构性缺陷验证报告

日期2026-05-06 对照数据源

  • outputs/qwen3-30b-tp1-v5-optD-baseline-rerun/v5 KVC kv-aware Option D2P6D3 次同配置 rerun
  • outputs/qwen3-30b-tp1-exps/exp1_8way_dp_cache_aware_summary.json(同 trace 8DP CA
  • outputs/qwen3-30b-tp1-v5-optD-baseline-rerun/.../logs/decode-{0..5}.logprefill-{0,1}.log 模型Qwen3-30B-A3BTP1单机 8×H100 80GBtrace qwen35-swebench-50sess.jsonl4449 reqs / 52 sessions报告作用域:验证 docs/AGENTIC_FIT_ANALYSIS_ZH.md §1-§7 提出的结构性 claim 是否真实存在;量化影响。

⚠️ 环境限制:本轮缺 GPU 访问,未跑新 sweep。所有数据来自已存在的 v5 rerun + 8DP baseline。Backpressure 代码已实现但未端到端验证——下文标注为"预期收益pending GPU smoke"。


0. 实验有效性锚点N=1 不可信

3 次 v5 baseline EXP2完全相同配置)的 errors 漂移:

Run Errors Lat P50 Lat P90 TTFT P50
run1 372 1.11s 8.65s 0.147s
run2 912 0.94s 7.68s 0.071s
run3 396 1.22s 8.43s 0.183s

errors 漂移 2.5×372 → 912P50 latency 漂移 30%任何 N=1 比较 < 30% 差异都不可信。 后续所有"同 trace 不同配置 / 不同代码"的对比,都需要 N≥3 才有意义。

对 KVC vs DP 的 headline 数据3 次 KVC 的最佳值P50=0.94s)仍然是 DPP50=0.65s)的 1.45×——8 way DP 的优势远超 single-run variance 范围,这一头条结论不受 variance 影响。


§1. Session 永久 pin 到 D + 容量盲选 → 极端双峰 完全成立

Claim

KvAwarePolicy 评分以 hash overlap 为主,没有 D 容量项。Session 第一次落到某 D 后被永久 pin。导致大 session 在已满 D 上反复 admission 拒绝,小 session 在原 D 上 100% 走 direct-to-D。

数据

(a) Session 永久绑定,跨 3 次 rerun 一致

run1: 52 sessions, avg distinct-D-per-session = 1.00
run2: 52 sessions, avg distinct-D-per-session = 1.00
run3: 52 sessions, avg distinct-D-per-session = 1.00

每个 session 在整个运行中只访问 1 个 D worker3 次独立 run 完全一致。不是巧合,是结构。

(b) Direct-to-D 命中率呈极端双峰

Direct-to-D rate run1 run2 run3
0-20%(饿死) 15 18 16
20-40% 7 6 7
40-60% 11 7 9
60-80% 5 6 4
80-100%(顺利) 14 15 16

中间态稀少,两端拥挤。

(c) 跨 3 次 run 一致饿死的 session 与 session 大小强相关

13 sessions starved (<20% direct-to-D) in ALL 3 runs.
  avg peak input of consistently-starved sessions: 62043 tokens
  avg peak input of consistently-lucky sessions:   31344 tokens
  ratio: 1.98× — starved sessions are exactly 2× larger.

13/52 = 25% 的 session 在 3 次独立 run 中都被饿死,且这些 session 的 peak input 恰好是顺利 session 的 2 倍。 这排除了"运气"假说,证实是大 session 在容量过载 D 上结构性失败。

影响量化

  • 25% session 几乎每个 turn 都走 fallback 路径,相对 direct-to-D TTFT 慢 100×、E2E 慢 6×数据点fallback path mean lat ~3.5s vs direct ~0.5s
  • 对应这些 session 的用户体验是"系统性糟糕",而不是"偶尔慢"
  • SLO 视角下 P99 完全由这 13 个 session 拉高

结论

完全成立。修复方向不在本轮policy score 加 capacity penalty + 允许 session 跨 D 迁移,或 D 端引入 hot session retract。


§2. D 端 LRU 只 evict idle session → 跟不上压力 完全成立

Claim

scheduler.py:2040evict_idle_streaming_sessions_lru 只能 evict "所有 req 都 finished + streaming 模式"的 session。高并发下 hot session 永远不 idleLRU 找不到东西可踢。结果 D 顶到 100% 然后撞 mooncake transfer timeout。

数据v5 baseline rerun run1

D worker Trim 事件 KVTransferError 峰值 token_usage
decode-0 9 0 0.99
decode-1 43 4 0.99
decode-2 16 153 0.97
decode-3 37 29 0.99
decode-4 28 90 1.00
decode-5 30 93 1.00

6 个 D 全部峰值 ≥ 0.97,其中 2 个直接顶到 1.00KV 池完全耗尽)。LRU 触发 9-43 次,远不及 transfer 错误的 90-153 次。

decode-2 极端trim 16 次 vs error 153 次 = LRU 比错误慢 9.5×

影响量化

  • 单 run 累计 369 KVTransferError总 6 个 D 之和)
  • 对应 ~8% 的请求失败率v5 errors 9/372/912 三次平均 ~430/4449 = 9.7%
  • 每次 mooncake timeout 是 32s——对 P99 latency 直接贡献几十秒尾巴

结论

完全成立。修复方向(不在本轮):分层 eviction——除 idle 外加冷 session retract、按访问频率/时序加权。Backpressure本轮代码只是把"D 满"的雪崩从"timeout 错误"转成"主动等待"不是真正解决容量问题


§3. 没有 D→Replay backpressure 通道 成立(已实现修复)

Claim

D 端 transfer queue 堆 → 32s timeout → KVTransferError没有"D 过载请慢点"信号反向到 replayconcurrency 一直 32 不降。

数据

  • §2 的 369 KVTransferError 全部为 32s mooncake timeout日志中均为 Failed to send kv chunkDecode instance could be dead
  • 错误集中在运行后半段(按现有 KVC_DEBUG_JOURNEY_V1_TO_V5.md §v4错误均在 run 的 44.8% 之后开始累积)
  • 表明:前期 D 容量充裕时正常,达到容量上限后所有后续请求集中失败——典型无 backpressure 系统行为

修复(本轮已实现,待 GPU smoke 验证)

代码改动:

  1. third_party/sglang/python/sglang/srt/managers/io_struct.pyDirectAppendAdmissionReqOutput 增加 recommended_pause_ms 字段
  2. third_party/sglang/python/sglang/srt/managers/scheduler.py:admit_direct_append:基于 transfer_queue_depthretracted_queue_depthtoken_usage_after 计算 hint
    def _compute_backpressure_pause_hint(...):
        if retracted_queue_depth > 0: return 1500
        if token_usage_after >= 0.90: return max(200, min(2000, overshoot * 5))
        if transfer_queue_depth >= 8: return min(2000, transfer_queue_depth * 100)
        return 0
    
  3. src/agentic_pd_hybrid/replay.py
    • DecodeResidencyState.pause_until_s: dict[str, float]
    • _query_decode_direct_admission 解析 hint 更新 pause_until_s
    • 新增 _wait_for_decode_pause,在 _invoke_router / _invoke_session_direct 入口检查
  4. CLI flag--enable-backpressure--backpressure-max-pause-s 2.0(默认关闭)
  5. 结构性日志:structural/admission-events.jsonlbackpressure-events.jsonlsession-d-binding.jsonl

预期收益pending GPU smoke E2 vs E1

  • KVTransferError 应从 ~370 / 4449 跌到 < 50 / 4449
  • P99 应改善(消除 32s timeout 尾巴)
  • 整体 latency mean 可能略升(被强制 pause但 P99 应大幅降
  • backpressure-events.jsonl 应显示 D-4 / D-5 累积大量 pause 事件(与 §2 数据吻合)

结论

Claim 成立;修复已实现,待 smoke 验证。注意backpressure 是降级机制,不是性能优化——它把"硬错误"换成"主动等待",整体 throughput 不会因此提升。


§4. Admission RPC 与 scheduler 主循环耦合 ⚠️ 间接证据,本轮未直接验证

Claim

admit_direct_append 进 scheduler 主循环遍历 session slotadmission RPC 频率 16+/s 时与 decode 抢调度。

现有间接证据

  • docs/V5_PROFILE_INVESTIGATION_ZH.md:仅加 1Hz /server_info polling 就让 EXP2 errors 从 9 涨到 41546×但 v6 P0 三次 baseline 不开 polling 同样得到 372/912/396——polling 不是唯一原因,主循环负载本身就敏感

本轮未做

  • 没有"admission probe 拆 fast/slow"的对照实验。需要 SGLang 较深的改动(提供 lock-free snapshot不在 KISS 边界。

结论

Claim 间接成立,本轮未直接验证。Backpressure 实现里 admission RPC 的频率没有变(仍每个 turn 一次),只是结果会触发 sleep。如果这条 claim 成立,加 backpressure 后 admission RPC 数量大致不变但每次响应里的 pause_ms 会非零——新增的 admission-events.jsonl 可在 GPU smoke 后用来直接验证此现象


§5. P-side round-robin 不感知 D 健康 成立

Claim

pd_router.py:_select_decode_index 是裸 round-robin。任一 P 撞到 hot D 时反复失败,另一 P 完全不受影响。

数据v5 baseline rerun run1

Worker KVTransferError "Decode could be dead"
prefill-0 367 361
prefill-1 2 0

prefill-0 的请求量从 summary 看是 2225 vs prefill-1 的 2224——请求量近乎对半,错误率差 180×

影响量化

  • 失败请求集中在 P-0 → 某个 hot D 的链路上(日志中反复出现 to 10.45.80.47:XXXXX
  • 单 P 的"死亡链路"贡献了 99% 的全部 KVTransferError
  • 如果 P 选择能避开"正在和 hot D 死磕"的链路,理论上可消除单 P 故障的雪崩效应

备注

  • 此现象未在 v6 P0 的 3 次 rerun 中横向验证——只有 run1 的日志可读。需要在新 sweep 的 prefill-{0,1}.log 上重复确认,避免 N=1 嫌疑。

结论

单 run 数据成立,多 run 一致性未验证。修复方向不在本轮router 选 P 时考虑 (P 当前 inflight transfer 数, 目标 D 健康度)。


§6. 已撤回Replay 端 session footprint 估算膨胀

写计划时仔细看代码后撤回——_estimate_session_resident_tokens 返回 full prompt但所有需要"增量"的 call site (replay.py:1247-1254:1393-1394:1490-1491) 都已用 target - current 减法处理。不是 bug


§7. time-scale=10 把 inter-turn gap 压到 1/10 完全成立

数据

原始 trace inter-turn gap (n=4397):
  p10=1.6s   p50=2.5s   p90=7.8s   p99=25.1s   max=261s

time-scale=10 实际 replay gap:
  p10=0.16s  p50=0.25s  p90=0.78s  p99=2.5s    max=26s

真实 agentic 用户/agent 在 turn 之间停 2-8 秒思考、打字、tool call、agent reasoning。time-scale=10 把这些窗口压到 0.16-0.78 秒——人为消除了 D 的自然 idle 时间,正好是 KVC 想利用的"session 短暂 idle 时 LRU 可以 evict、其他 session 可以 admit"机会。

测量学影响

  • 所有 v3-v6 数据基于 time-scale=10
  • 意味着所有"KVC 在 SWE 上输给 baseline"的结论可能被 benchmark 放大了
  • §1 的 25% session 永久饿死现象,在 time-scale=1 下可能因为 D 有更多 drain 时间而显著缓解

本轮未做

  • 没跑 time-scale=1 baseline。这是项目当前最重要但缺失的验证
  • Smoke sweep 脚本(scripts/sweep_backpressure_smoke.shE3、E4 包含了 time-scale=1 的 KVC + DP 短 trace 对比,等 GPU 时跑。

结论

Claim 完全成立time-scale=1 验证为 P0 待办


头条对比(同 trace、同硬件

8-way DP cache-aware (TP1):
  errors=  0 | latency mean=1.426s p50=0.654s p90=3.609s
              | TTFT  mean=0.123s p50=0.093s p90=0.256s

KVC v5 2P6D (3 reruns, no polling):
  run1: errors=372 | mean=3.50s p50=1.11s p90=8.65s | TTFT mean=2.13s
  run2: errors=912 | mean=3.00s p50=0.94s p90=7.68s | TTFT mean=1.64s
  run3: errors=396 | mean=3.42s p50=1.22s p90=8.43s | TTFT mean=2.07s

KVC 三次 run 全输 DP且差距远超 single-run variance

  • Latency meanDP 优 +110%KVC 平均 3.30s vs DP 1.43s
  • Latency P50DP 优 +65%KVC 平均 1.09s vs DP 0.65s
  • TTFT meanDP 优 +1500%KVC 平均 1.95s vs DP 0.12s——慢 17×
  • ErrorsDP 0 vs KVC 平均 ~560

这是这个项目当前最严肃的事实——所有 KVC 复杂度回报为负。


综合结论

按"是否结构性 + 影响大小"的二维分类:

Claim 结构性 影响 本轮验证 修复KISS 内) 修复KISS 外)
§1 Session pin + 容量盲选 25% session 饿死) 3 run 一致 capacity-aware policy + 跨 D 迁移
§2 LRU 跟不上 大(每次 ~370 KVTransferError 6 D 数据 分层 eviction、hot retract
§3 无 backpressure 中-大(消除 32s timeout 雪崩) ⚠️ 已实现,待 smoke 本轮交付
§4 admission RPC 干扰 弱-中 ⚠️ 间接 probe / commit_evict 拆分
§5 P-side 不感知 D 健康 中(单 P 错误率差 180× N=1需 N≥3 复核 router P 选择带 D 健康反馈
§6 estimate 膨胀 已撤回
§7 time-scale=10 失真 强(测量学) 大(可能颠覆所有 KVC vs DP 结论) 数据明确 改 flag

最关键的两个 takeaway

  1. §7 time-scale=1 是当前项目所有结论的前置依赖——必须先做。如果 time-scale=1 下 KVC 与 DP 接近,前面所有 v3-v6 的"KVC 输得彻底"诊断都需要重新解读。
  2. §1 + §2 是双胞胎结构性问题——session 被永久 pin 在某个 D + D 不能 evict 已满 = 大 session 永久卡死。任何不动 policy + 不动 LRU 的修复(包括本轮的 backpressure只能让症状好看不能消除根因。

本轮代码改动汇总git diff 范围)

src/agentic_pd_hybrid/replay.py        # +结构性日志 + backpressure pause 检查 + admission 增强
src/agentic_pd_hybrid/cli.py           # +CLI flags
src/agentic_pd_hybrid/benchmark.py     # +CLI flags 透传
third_party/sglang/python/sglang/srt/managers/io_struct.py
third_party/sglang/python/sglang/srt/managers/scheduler.py
                                       # +recommended_pause_ms 字段 + hint 计算
scripts/sweep_backpressure_smoke.sh    # 4-run smoke sweep待 GPU 跑)
scripts/analysis/analyze_backpressure_smoke.py
                                       # 配套分析器
docs/REFACTOR_PLAN_ZH.md               # 计划文档
docs/STRUCTURAL_VALIDATION_REPORT_ZH.md
                                       # 本报告

代码默认行为不变enable_backpressure=False)——所有现有脚本/配置无影响。


待 GPU 时执行

bash scripts/sweep_backpressure_smoke.sh
python3 scripts/analysis/analyze_backpressure_smoke.py outputs/sweep_backpressure_smoke

预算4 个 run × 30-60 min ≈ 3-4h GPU 时间。

按 §3 的预期E2 (KVC + backpressure) 相对 E1 (KVC baseline) 应有 errors 降 70%+P99 改善TTFT P50 持平或略升。E3 (KVC + backpressure @ time-scale=1) vs E4 (DP @ time-scale=1) 是验证 §7 的关键对照。

如果 E2 vs E1 的 errors 没有显著下降,说明 backpressure hint 公式调得不对(_compute_backpressure_pause_hint 阈值可调),或 §3 实际不是雪崩主因(更可能是 §2 D-side LRU 才是)。