#!/usr/bin/env python3 """Cross-comparison of E1 (naive PD), E3 (KVC v2 + load-floor), E4 (KVC + D→P). Usage: uv run --no-sync python scripts/analyze_e4_d_to_p.py \ --e1 outputs/e1_naive_1p3d_kvaware_rdma_50sess/e1_naive_1p3d_kvaware_run1_summary.json \ --e3 outputs/e3_kvc_v2_loadfloor_rdma_50sess/*_summary.json \ --e4 outputs/e4_kvc_v2_d_to_p_sync_50sess/e4_kvc_v2_d_to_p_sync_run1_summary.json \ --e4-metrics outputs/e4_kvc_v2_d_to_p_sync_50sess/e4_kvc_v2_d_to_p_sync_run1_metrics.jsonl """ from __future__ import annotations import argparse import glob import json import statistics from collections import Counter, defaultdict from pathlib import Path from typing import Any def _load_summary(path_glob: str) -> dict[str, Any] | None: paths = glob.glob(path_glob) if not paths: return None with open(paths[0]) as f: return json.load(f) def _percentiles(values: list[float]) -> dict[str, float]: if not values: return {"p50": 0, "p90": 0, "p99": 0, "mean": 0} values = sorted(values) n = len(values) return { "mean": statistics.mean(values), "p50": values[n // 2], "p90": values[min(n - 1, int(n * 0.90))], "p99": values[min(n - 1, int(n * 0.99))], } def _row(label: str, s: dict[str, Any] | None, key: str) -> str: if s is None: return f" {label:<40} (missing)" stat = s.get(key, {}) return ( f" {label:<40} " f"mean={stat.get('mean', 0):>8.3f} " f"p50={stat.get('p50', 0):>8.3f} " f"p90={stat.get('p90', 0):>8.3f} " f"p99={stat.get('p99', 0):>8.3f}" ) def main(): ap = argparse.ArgumentParser() ap.add_argument("--e1", required=True) ap.add_argument("--e3", required=True) ap.add_argument("--e4", required=True) ap.add_argument("--e4-metrics", help="optional path to e4 metrics.jsonl for reseed-mode breakdown") args = ap.parse_args() e1 = _load_summary(args.e1) e3 = _load_summary(args.e3) e4 = _load_summary(args.e4) print("=" * 90) print("E1 / E3 / E4 cross-comparison") print("=" * 90) for s, name in [(e1, "E1"), (e3, "E3"), (e4, "E4")]: if s is None: print(f" {name}: MISSING") continue total = (s.get("error_count", 0) + s.get("abort_count", 0) + sum(c for c in s.get("execution_modes", {}).values())) print(f" {name}: error={s.get('error_count', 0):>4} abort={s.get('abort_count', 0):>4} " f"failure={s.get('failure_count', 0):>4} exec_modes={dict(s.get('execution_modes', {}))}") print("\n--- latency_stats_s ---") print(_row("E1 naive PD", e1, "latency_stats_s")) print(_row("E3 KVC v2 LF", e3, "latency_stats_s")) print(_row("E4 KVC + D→P", e4, "latency_stats_s")) print("\n--- ttft_stats_s ---") print(_row("E1 naive PD", e1, "ttft_stats_s")) print(_row("E3 KVC v2 LF", e3, "ttft_stats_s")) print(_row("E4 KVC + D→P", e4, "ttft_stats_s")) print("\n--- per-decode load ---") for s, name in [(e1, "E1"), (e3, "E3"), (e4, "E4")]: print(f" {name}: {dict(s.get('per_decode_load', {}) if s else {})}") # ---- E4 reseed-mode breakdown ---- if args.e4_metrics: print("\n--- E4 reseed-mode breakdown (from metrics.jsonl) ---") try: modes = defaultdict(list) d2p_outcomes = Counter() with open(args.e4_metrics) as f: for line in f: try: rec = json.loads(line) except json.JSONDecodeError: continue mode = rec.get("execution_mode") or "?" ttft = rec.get("ttft_s") if ttft is not None: modes[mode].append(float(ttft)) # D→P hit counter (we logged via logger.info, not in metrics # — placeholder for future structured event) print(f" per-mode TTFT (count, mean, p50, p99):") for mode, ttfts in sorted(modes.items()): p = _percentiles(ttfts) print(f" {mode:<55} n={len(ttfts):>4} " f"mean={p['mean']:>7.3f} p50={p['p50']:>7.3f} p99={p['p99']:>7.3f}") except Exception as e: print(f" parse error: {e}") # ---- H1 / H2 / H3 verdicts ---- print("\n" + "=" * 90) print("Hypothesis verdicts") print("=" * 90) if e1 and e4: e1_p99 = e1.get("ttft_stats_s", {}).get("p99", float("inf")) e4_p99 = e4.get("ttft_stats_s", {}).get("p99", float("inf")) verdict_h1 = "PASS" if e4_p99 <= e1_p99 else "FAIL" print(f" H1 (E4 TTFT p99 ≤ E1 TTFT p99): {e4_p99:.3f} vs {e1_p99:.3f} → {verdict_h1}") if e3 and e4: e3_modes = e3.get("execution_modes", {}) e4_modes = e4.get("execution_modes", {}) e3_success = sum(v for k, v in e3_modes.items() if "reseed" not in k.lower()) e4_success = sum(v for k, v in e4_modes.items() if "reseed" not in k.lower()) verdict_h3 = "PASS" if (e4_success or 0) >= 0.85 * (e3_success or 1) else "FAIL" print(f" H3 (E4 success count ≥ 0.85 × E3 success): " f"{e4_success} vs 0.85 × {e3_success} = {0.85 * e3_success:.0f} → {verdict_h3}") if __name__ == "__main__": main()