Files
agentic-kvc/analysis/pd_sep_paper_section/README.md
Gahow Wang 21ffb3d4f7 PD-sep matrix infrastructure: bench.sh pdsep mode + matrix driver
Adds the experiment harness that gates the empirical claims (C2/C3/C4/C5)
in the PD-sep paper section. Three pieces:

  1. scripts/bench.sh: new --mode pdsep with --pd-ratio P:D, and an
     --eager flag to re-enable --enforce-eager for the cuda-graph
     ablation. pdsep reuses the elastic-mode Mooncake kv_both launch and
     swaps the proxy command from --combined to --prefill/--decode.
     baseline and elastic flows are unchanged.

  2. analysis/pd_sep_paper_section/scripts/bench_pd_matrix.sh: matrix
     driver that runs {combined-ca, pdsep-4p4d, pdsep-6p2d} x cudagraph
     x 3 seeds by default (~2 h on dash0). --with-rr adds combined-rr;
     --with-eager doubles to ~5 h with the cuda-graph ablation. Skips
     completed runs, captures per-instance vLLM logs (needed for C3
     step-level KV-utilization mining).

  3. fig_kv_memory_wall.pdf: empirical anchor (star) at REPORT.md §3.3's
     observed 6P+2D 97% KV utilization. The marker lands on the model's
     predicted curve at p90 input, confirming the steady-state analysis.

README updated with the run command, output layout, and the followup
plotters that consume outputs/pd_matrix/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:47:33 +08:00

144 lines
7.2 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.

# Paper section: PD separation under agentic workloads
This directory collects everything produced for the "PD-sep is net negative
on agentic workloads" paper section. It is one section of a larger paper,
not the whole paper.
## Layout
```
analysis/pd_sep_paper_section/
├── README.md # this file
├── system_analysis.md # why PD-sep loses despite compute-bound prefill (6 layers)
├── scripts/
│ ├── plot_workload.py # C1: input/output CDF + KV reuse decomposition
│ ├── plot_roofline.py # C6: prefill roofline at varying cache reuse
│ ├── plot_routing_lever.py # C7: routing vs PD-sep as design levers
│ ├── plot_kv_memory_wall.py # KV mem-wall model + empirical anchor
│ └── bench_pd_matrix.sh # orchestrates the C2/C3/C4/C5 experiment matrix on dash0
└── figures/
├── fig_c1a_io_cdf.pdf # input/output token CDF (from traces/w600_r0.0015_st30.jsonl)
├── fig_c1b_reuse.pdf # KV reuse decomposition: 79% intra-session
├── fig_c6_roofline.pdf # analytical roofline
├── fig_c7_routing_lever.pdf # routing vs PD-sep (legacy data, footer caveat)
└── fig_kv_memory_wall.pdf # the explanatory figure for system_analysis.md
```
## Candidate claims -> figures (status)
| Claim | Figure | Status |
|---|---|---|
| C1a: agentic input distribution (p50=33.5k, p90=101k, p99=132k); I/O = 142x | `figures/fig_c1a_io_cdf.pdf` | **rendered** |
| C1b: 79% intra-session reuse + 0.8% cross-session | `figures/fig_c1b_reuse.pdf` | **rendered** |
| C2: PD-sep vs Combined headline numbers | (not yet) | **needs re-run without --enforce-eager on `traces/w600_r0.0015_st30.jsonl`** |
| C3: decode KV cache memory wall (time-series) | (not yet) | needs step-level vLLM telemetry during PD-sep run |
| C4: TTFT stacked breakdown (prefill / KV pull / decode wait) | (not yet) | needs per-request breakdown.json from PD-sep run |
| C5: cuda-graph ablation (eager vs cudagraph × Combined vs PD-sep) | (not yet) | needs the 2×2 matrix |
| C6: prefill stays compute-bound at 95% reuse | `figures/fig_c6_roofline.pdf` | **rendered** |
| C7: cache-aware routing is a larger lever than PD-sep | `figures/fig_c7_routing_lever.pdf` | **rendered** (legacy data, footer caveat) |
| KV-WALL: per-D-instance KV demand vs PD layout (system mechanism) | `figures/fig_kv_memory_wall.pdf` | **rendered** (analytical, audit constants in script) |
## System-level argument (`system_analysis.md`)
The doc answers: *if prefill stays compute-bound even at 95% reuse, why
does PD separation not help?* Six layers, each pointing to a figure in
this directory:
1. compute-bound is a *kernel* property, not a system claim
2. absolute prefill work after cache hit is small (~hundreds of ms savings ceiling)
3. PD separation relocates compute; it doesn't accelerate it
4. PD separation's costs (KV transfer, decode-side concentration) scale with workload size
5. **decode-side KV memory wall** — quantified in `fig_kv_memory_wall.pdf`
6. the DistServe / Splitwise assumption that silently breaks: `concurrent × KV/req / (N_D × HBM)` is ≪ 1 for chatbot but ≥ 1 for agentic at p90+ context
## In-place edits made for this task
These edits are in the repo, not in this directory, because they modify
existing launch scripts. `--enforce-eager` was removed so cuda graphs can be
captured — PD-sep's D-node is a particularly clean case for cuda-graph
benefit and the prior methodology suppressed it.
| File | Lines | Change |
|---|---|---|
| `scripts/bench.sh` | 150, 161 | drop `--enforce-eager` (elastic + baseline modes) |
| `scripts/launch_pd_mooncake.sh` | 47, 64 | drop `--enforce-eager` (P and D instances) |
| `scripts/launch_pd_separated.sh` | 52, 68 | drop `--enforce-eager` (P and D instances) |
| `scripts/launch_phase1_ps.sh` | 32, 43 | drop `--enforce-eager` (C and PS instances) |
| `scripts/launch_elastic_p2p.sh` | 57 | drop `--enforce-eager` (kv_both instances) |
`scripts/legacy/*.sh` are intentionally left as-is — they record the
configuration of past experiments.
`REPORT.md` and `analysis/pd_separation_analysis.md` still describe the
old `--enforce-eager` setup. Update them once the new runs land.
## Reproducing the figures
From repo root:
```bash
# C1 (needs traces/w600_r0.0015_st30.jsonl; ~1.2 MB, pull from dash0 if missing)
.venv/bin/python analysis/pd_sep_paper_section/scripts/plot_workload.py \
--trace traces/w600_r0.0015_st30.jsonl
# C6 (analytical, runs anywhere with matplotlib)
.venv/bin/python analysis/pd_sep_paper_section/scripts/plot_roofline.py
# C7 (hardcoded REPORT.md §3.1 numbers; no inputs)
.venv/bin/python analysis/pd_sep_paper_section/scripts/plot_routing_lever.py
# KV mem-wall (analytical; audit constants at top of the script)
.venv/bin/python analysis/pd_sep_paper_section/scripts/plot_kv_memory_wall.py
```
All four default `--outdir` to `analysis/pd_sep_paper_section/figures`.
## Running the experiment matrix (gating C2/C3/C4/C5)
`bench_pd_matrix.sh` orchestrates the experiments that the unrendered
claims depend on. It uses the extended `scripts/bench.sh` (now supports
`--mode pdsep --pd-ratio 4:4|6:2` and `--eager` for the cuda-graph
ablation; all launchers no longer pin `--enforce-eager`).
On dash0:
```bash
cd ~/agentic-kv
# minimal set: 3 configs (combined-ca, pdsep-4p4d, pdsep-6p2d) x 3 seeds
# = 9 runs ~= 2 h
bash analysis/pd_sep_paper_section/scripts/bench_pd_matrix.sh
# full matrix (adds combined-rr and the eager ablation)
bash analysis/pd_sep_paper_section/scripts/bench_pd_matrix.sh \
--with-rr --with-eager
```
Each run writes to `outputs/pd_matrix/<config>_<mode>_seed<N>/` with
`metrics.summary.json`, `breakdown.json`, `apc.txt`, `gpu_util.csv`, and
per-instance vLLM logs (the latter contain the step-level
`KV cache: X%` lines needed for the C3 time-series figure).
## Caveats / open items
- **C7 uses legacy data**. The footer of `fig_c7_routing_lever.pdf` says so:
PD-sep numbers come from the random-sampled trace + `--enforce-eager`.
After `pd_matrix` lands, swap the four numbers in `plot_routing_lever.py`'s
`ROWS` table and re-render.
- **C2/C3/C4/C5 figures depend on `pd_matrix` outputs**. Followup plotters
(TBD) will read `outputs/pd_matrix/*/metrics.summary.json`,
`breakdown.json`, and the `KV cache: X%` lines from per-instance logs to
produce: bar chart with error bars (C2), KV utilization time-series (C3),
TTFT stacked breakdown (C4), 2x2 cuda-graph ablation (C5).
- **C8 (mined logs)**: rejected — existing PD-sep `outputs/exp3_*` directories
have per-request metrics but no per-stage breakdown and no step-level KV
utilization. C3/C4 require fresh runs with proxy `/breakdown` collection
(already automatic in `bench.sh collect_artifacts()`).
- **C6 is analytical**, so it is independent of any re-run. The numbers
match `scripts/compute_roofline.py` (constants are duplicated; if one
changes, the other must change too).
- **fig_kv_memory_wall.pdf** is analytical with one empirical anchor (the
star marker for REPORT.md §3.3 6P+2D @ 97 % KV utilization). It does not
need a re-run, but the empirical anchor's *pinpoint* would be more
rigorous from a `pd_matrix` 6P+2D log (KV-utilization time-series rather
than the single snapshot).