#!/usr/bin/env python3 """Aggregate MB1 results: per-(D, P) baseline vs during-prefill effective TPOT. The driver's `tpot_during_prefill_p50_ms` is computed per-token and can be misleading: chunked-prefill schedules decode alongside each prefill chunk, so most decode-token intervals during the prefill burst look "normal" — but each chunk completion creates a long-stall token. p50 hides this, p90 exposes it, but the BEST single-number summary of "how much was decode slowed by prefill" is the *effective TPOT during the prefill burst*: effective_TPOT_during = prefill_ttft_ms / (num_tokens_during_prefill / D) i.e. wall-clock time divided by per-stream tokens emitted in that window. This captures the true average throughput of each decode stream while a prefill burst is underway. Compared to baseline_TPOT it gives the "phase-interference penalty" PD-disagg could in principle recover. """ from __future__ import annotations import argparse import csv import json import statistics from collections import defaultdict from pathlib import Path def main() -> None: p = argparse.ArgumentParser() p.add_argument("--summary", type=Path, required=True) p.add_argument("--out", type=Path, required=True) args = p.parse_args() rows = list(csv.DictReader(args.summary.open())) by_dp: dict[tuple[int, int], list[dict]] = defaultdict(list) for r in rows: D = int(r["decode_batch_size"]) P = int(r["new_prefill_tokens"]) by_dp[(D, P)].append(r) summary = [] for (D, P) in sorted(by_dp): rs = by_dp[(D, P)] base = statistics.mean(float(r["tpot_baseline_p50_ms"]) for r in rs) during_p50_vals = [float(r["tpot_during_prefill_p50_ms"]) for r in rs if float(r["tpot_during_prefill_p50_ms"]) > 0] during_p90_vals = [float(r["tpot_during_prefill_p90_ms"]) for r in rs if float(r["tpot_during_prefill_p90_ms"]) > 0] ttft_vals = [float(r["prefill_ttft_ms"]) for r in rs] n_tok_vals = [float(r["num_tokens_during_prefill"]) for r in rs if float(r["num_tokens_during_prefill"]) > 0] if not n_tok_vals or D == 0: continue ttft = statistics.mean(ttft_vals) n_tok_total = statistics.mean(n_tok_vals) per_stream_tokens = n_tok_total / D eff_tpot_during = ttft / per_stream_tokens if per_stream_tokens > 0 else 0 penalty_x = eff_tpot_during / base if base > 0 else 0 # PD-disagg potential benefit (per stream, ms): # if decode ran at baseline rate throughout the prefill window, # it would emit ttft/baseline tokens. Actual is per_stream_tokens. # Time saved if no interference = ttft - per_stream_tokens * baseline time_saved_per_stream = ttft - per_stream_tokens * base summary.append({ "decode_batch_size": D, "new_prefill_tokens": P, "baseline_tpot_ms": round(base, 2), "during_tpot_p50_ms_raw": (round(statistics.mean(during_p50_vals), 2) if during_p50_vals else None), "during_tpot_p90_ms_raw": (round(statistics.mean(during_p90_vals), 2) if during_p90_vals else None), "prefill_ttft_ms": round(ttft, 1), "num_tokens_during_prefill_total": round(n_tok_total, 1), "per_stream_tokens_during": round(per_stream_tokens, 2), "effective_tpot_during_ms": round(eff_tpot_during, 1), "interference_penalty_x": round(penalty_x, 1), "max_pd_disagg_benefit_ms_per_stream": round(time_saved_per_stream, 1), }) args.out.parent.mkdir(parents=True, exist_ok=True) args.out.write_text(json.dumps({"summary": summary}, indent=2)) print(f"{'D':>3} {'P':>7} {'base_ms':>9} {'eff_during_ms':>15} " f"{'penalty':>10} {'pd_benefit_ms':>15}") for s in summary: print(f"{s['decode_batch_size']:>3} {s['new_prefill_tokens']:>7} " f"{s['baseline_tpot_ms']:>9.2f} " f"{s['effective_tpot_during_ms']:>15.1f} " f"{s['interference_penalty_x']:>9.1f}x " f"{s['max_pd_disagg_benefit_ms_per_stream']:>15.0f}") print(f"\nwrote {args.out}") if __name__ == "__main__": main()