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

305 lines
15 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.

# 结构性缺陷验证报告
**日期**2026-05-06
**对照数据源**
- `outputs/qwen3-30b-tp1-v5-optD-baseline-rerun/`v5 KVC kv-aware Option D2P6D**3 次同配置 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}.log``prefill-{0,1}.log`
**模型**Qwen3-30B-A3BTP1单机 8×H100 80GBtrace `qwen35-swebench-50sess.jsonl`4449 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 不同配置 / 不同代码"的对比都需要 N3 才有意义
**对 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:2040` `evict_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 chunk` `Decode 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.py``DirectAppendAdmissionReqOutput` 增加 `recommended_pause_ms` 字段
2. `third_party/sglang/python/sglang/srt/managers/scheduler.py:admit_direct_append`基于 `transfer_queue_depth``retracted_queue_depth``token_usage_after` 计算 hint
```python
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.jsonl`、`backpressure-events.jsonl`、`session-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.sh`E3、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
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 才是)。