"""Aggregate B2 microbench cells: same- vs different-worker prefill overlap. For each (variant × prefill_size) cell we have: - 240 short-prompt decode requests at qps=4 - 4 large-prompt one-token "prefill injections" The interesting question is *not* "does any other request's prefill overlap this decode" (the answer is always yes — every decode begins with its own short prefill, and at qps=4 they overlap each other constantly). The interesting question is "does an injected large prefill on the *same* worker materially slow this decode down?". So we: 1) extract each cell's injection windows = [(t_dispatch, t_finish) for r in metrics if r.workload=="prefill"]; 2) label each decode request as overlap iff its [t_first_token, t_finish] intersects at least one injection window; 3) compute TPOT p50/p90/p99 for overlap vs clean; 4) the per-cell interference index = TPOT_p90(overlap) / TPOT_p90(clean). For "different" variant this should hover near 1.0; for "same" it should rise with prefill_size. """ from __future__ import annotations import argparse import json from collections import defaultdict from pathlib import Path from analysis.characterization.joined_analysis import ( _percentile, load_jsonl, write_json, ) def _overlaps(a_start: float, a_end: float, b_start: float, b_end: float) -> bool: return a_start <= b_end and b_start <= a_end def _analyze_cell(metrics_rows: list[dict]) -> dict: prefills = [r for r in metrics_rows if r.get("workload") == "prefill" and r.get("error") is None] decodes = [r for r in metrics_rows if r.get("workload") == "decode" and r.get("error") is None] inj_windows: list[tuple[float, float]] = [] for p in prefills: ts = p.get("t_dispatch_unix") te = p.get("t_finish_unix") if ts is None or te is None: continue inj_windows.append((float(ts), float(te))) overlap_tpots: list[float] = [] clean_tpots: list[float] = [] overlap_ttfts: list[float] = [] clean_ttfts: list[float] = [] for d in decodes: ts = d.get("t_dispatch_unix") te = d.get("t_finish_unix") if ts is None or te is None: continue is_overlap = any(_overlaps(ts, te, ws, we) for ws, we in inj_windows) tpot = d.get("tpot_s") ttft = d.get("ttft_s") if tpot is not None: (overlap_tpots if is_overlap else clean_tpots).append(float(tpot)) if ttft is not None: (overlap_ttfts if is_overlap else clean_ttfts).append(float(ttft)) p90_overlap = _percentile(overlap_tpots, 0.90) if overlap_tpots else None p90_clean = _percentile(clean_tpots, 0.90) if clean_tpots else None idx = (p90_overlap / p90_clean) if (p90_overlap and p90_clean) else None return { "n_prefill_injections": len(prefills), "n_decode_total": len(decodes), "n_decode_overlap": len(overlap_tpots), "n_decode_clean": len(clean_tpots), "tpot_p50_overlap_s": _percentile(overlap_tpots, 0.50), "tpot_p90_overlap_s": p90_overlap, "tpot_p99_overlap_s": _percentile(overlap_tpots, 0.99), "tpot_p50_clean_s": _percentile(clean_tpots, 0.50), "tpot_p90_clean_s": p90_clean, "tpot_p99_clean_s": _percentile(clean_tpots, 0.99), "ttft_p90_overlap_s": _percentile(overlap_ttfts, 0.90) if overlap_ttfts else None, "ttft_p90_clean_s": _percentile(clean_ttfts, 0.90) if clean_ttfts else None, "interference_index": idx, } def main() -> None: p = argparse.ArgumentParser(description="B2 sweep aggregation (window-overlap)") p.add_argument("--sweep-dir", type=Path, required=True) p.add_argument("--out", type=Path, default=None) args = p.parse_args() rows: list[dict] = [] for variant_dir in sorted(args.sweep_dir.glob("*/")): if variant_dir.name in ("logs",): continue for cell_dir in sorted(variant_dir.glob("p*/")): window_path = cell_dir / "run_window.json" metrics_path = cell_dir / "metrics.jsonl" if not window_path.exists() or not metrics_path.exists(): continue window = json.loads(window_path.read_text()) metrics_rows = load_jsonl(metrics_path) cell = _analyze_cell(metrics_rows) rows.append({ "variant": variant_dir.name, "prefill_size": int(window["prefill_size"]), "decode_endpoint": window["decode_endpoint"], "prefill_endpoint": window["prefill_endpoint"], **cell, }) out_path = args.out or args.sweep_dir / "b2_sweep_summary.json" write_json(out_path, {"rows": rows}) print(json.dumps(rows, indent=2)) if __name__ == "__main__": main()