"""Render PNG figures for the elastic_migration_v2 section. Inputs in ./data/ : - b3_policy_comparison.json - breakdown_unified.json, breakdown_unified_kv_both.json, breakdown_unified_v2.json, breakdown_unified_v2_strict.json - per_worker_.json for each of the four Outputs in ./figures/ : - fig_kv_both_overhead.png — three-way latency bars (plain vs kv_both vs v2) - fig_v2_trigger_funnel.png — request count per fall-through reason - fig_v2_predicted_vs_actual.png — cost-model migrate prediction vs realized TTFT - fig_three_way_hotspot.png — per-worker TTFT p90 grouped bars """ from __future__ import annotations import json from collections import Counter from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt ROOT = Path(__file__).parent DATA = ROOT / "data" OUT = ROOT / "figures" OUT.mkdir(parents=True, exist_ok=True) def _load(name: str): return json.loads((DATA / name).read_text()) POLICY_COLORS = { "unified": "#2ca02c", "unified_kv_both": "#9467bd", "unified_nixl_both": "#1f77b4", "unified_v2": "#d62728", "unified_v2_strict": "#ff7f0e", } def fig_kv_both_overhead(): comp = _load("b3_policy_comparison.json") by = {r["policy"]: r for r in comp["rows"]} pols = ["unified", "unified_kv_both", "unified_nixl_both", "unified_v2"] metrics = [ ("TTFT p90 (s)", lambda r: r["ttft_p90_s"]), ("TPOT p90 (ms)", lambda r: r["tpot_p90_s"] * 1000), ("E2E p90 (s)", lambda r: r["e2e_p90_s"]), ("hotspot index", lambda r: r["hotspot_index_ttft_p90"]), ] fig, axes = plt.subplots(1, 4, figsize=(15, 4.2)) for ax, (label, fn) in zip(axes, metrics): vals = [fn(by[p]) for p in pols] labels_short = [p.replace("unified_", "") for p in pols] labels_short[0] = "plain" bars = ax.bar(labels_short, vals, color=[POLICY_COLORS[p] for p in pols], edgecolor="black", linewidth=0.5) ax.set_title(label) ax.tick_params(axis="x", rotation=15, labelsize=9) for b, v in zip(bars, vals): ax.text(b.get_x() + b.get_width() / 2, v, f"{v:.2f}" if v < 100 else f"{v:.0f}", ha="center", va="bottom", fontsize=9) ax.grid(alpha=0.3, axis="y") baseline = vals[0] for i, v in enumerate(vals): if i == 0: continue pct = (v - baseline) / baseline * 100 ax.text(i, v * 0.5, f"{pct:+.0f}%", ha="center", fontsize=10, fontweight="bold", color="darkred" if pct > 0 else "darkgreen") fig.suptitle( "Mooncake substrate adds 19-45% across metrics; NIXL is 5-19pp better but\n" "still 16-38% above plain. v2's 5 PD-sep events don't recover the substrate tax." ) fig.tight_layout() fig.savefig(OUT / "fig_kv_both_overhead.png", dpi=120) plt.close(fig) def _bucket_reasons(data): """Collapse v2_reason strings into the funnel buckets.""" buckets = Counter() for r in data: if r.get("v2_pd_sep") is True: buckets["PD-sep TRIGGERED"] += 1 continue reason = (r.get("v2_reason") or "no_v2_reason").split(" (")[0] if reason.startswith("local_cost"): reason = "cost_benefit not enough margin" buckets[reason] += 1 return buckets def fig_v2_trigger_funnel(): strict = _load("breakdown_unified_v2_strict.json") relaxed = _load("breakdown_unified_v2.json") bs = _bucket_reasons(strict) br = _bucket_reasons(relaxed) order = [ "new_local_below_threshold", "chosen_no_active_decode", "chosen_few_decodes", "src_cache_below_threshold", "src_not_meaningfully_more_cache", "cost_benefit not enough margin", "PD-sep TRIGGERED", ] labels = [k for k in order if k in bs or k in br] strict_vals = [bs.get(k, 0) for k in labels] relaxed_vals = [br.get(k, 0) for k in labels] x = range(len(labels)) width = 0.4 fig, ax = plt.subplots(figsize=(11, 5)) ax.bar([i - width / 2 for i in x], strict_vals, width, label=f"v2.0 strict (PD-sep={bs['PD-sep TRIGGERED']}/{sum(bs.values())} " f"= {bs['PD-sep TRIGGERED']*100/sum(bs.values()):.2f}%)", color="#ff7f0e", edgecolor="black", linewidth=0.5) ax.bar([i + width / 2 for i in x], relaxed_vals, width, label=f"v2.1 relaxed (PD-sep={br['PD-sep TRIGGERED']}/{sum(br.values())} " f"= {br['PD-sep TRIGGERED']*100/sum(br.values()):.2f}%)", color="#d62728", edgecolor="black", linewidth=0.5) ax.set_xticks(list(x)) ax.set_xticklabels(labels, rotation=20, ha="right", fontsize=9) ax.set_ylabel("request count") ax.set_yscale("log") ax.set_title( "Why v2 rarely PD-seps: 88-76% of requests have new_local < threshold\n" "(intra-session cache already hot). Relaxing thresholds barely helps." ) ax.legend() ax.grid(alpha=0.3, axis="y", which="both") for i, (s, r) in enumerate(zip(strict_vals, relaxed_vals)): if s > 0: ax.text(i - width / 2, s * 1.05, str(s), ha="center", fontsize=8) if r > 0: ax.text(i + width / 2, r * 1.05, str(r), ha="center", fontsize=8) fig.tight_layout() fig.savefig(OUT / "fig_v2_trigger_funnel.png", dpi=120) plt.close(fig) def fig_v2_predicted_vs_actual(): """For each PD-sep'd request, plot model-predicted migrate cost vs realized TTFT. Should sit near y=x if model is calibrated; sits far above if mechanism is more expensive than modeled.""" relaxed = _load("breakdown_unified_v2.json") triggered = [r for r in relaxed if r.get("v2_pd_sep") is True] if not triggered: return predicted = [] actual = [] sizes = [] rids = [] for r in triggered: cm = r.get("v2_cost_migrate_s") t0 = r.get("t_proxy_recv") t_first = r.get("t_first_token") if cm is None or t0 is None or t_first is None: continue ttft = t_first - t0 predicted.append(cm) actual.append(ttft) sizes.append(r.get("input_length", 0)) rids.append(r.get("request_id", "?")) fig, ax = plt.subplots(figsize=(7, 5)) ax.scatter(predicted, actual, s=[max(100, sz / 100) for sz in sizes], color="#d62728", edgecolors="black", alpha=0.75) for p, a, sz, rid in zip(predicted, actual, sizes, rids): ax.annotate(f"input={sz}", (p, a), xytext=(8, 6), textcoords="offset points", fontsize=9) # y=x reference + 10x line + 20x line lo = 0.5 hi = max(50, max(actual) * 1.2) ax.plot([lo, hi], [lo, hi], "k--", alpha=0.5, label="y = x (calibrated)") ax.plot([lo, hi], [lo * 10, hi * 10], color="gray", linestyle=":", alpha=0.4, label="10x") ax.plot([lo, hi], [lo * 20, hi * 20], color="lightgray", linestyle=":", alpha=0.4, label="20x") ax.set_xscale("log") ax.set_yscale("log") ax.set_xlim(lo, hi) ax.set_ylim(lo, hi) ax.set_xlabel("Cost model: predicted migrate cost (s)") ax.set_ylabel("Realized TTFT (s)") ax.set_title( "All 5 PD-sep triggered requests in v2.1 sit far above y=x.\n" "Real transfer cost ~10-20x what the calibrated model predicted." ) ax.grid(alpha=0.3, which="both") ax.legend(loc="lower right") fig.tight_layout() fig.savefig(OUT / "fig_v2_predicted_vs_actual.png", dpi=120) plt.close(fig) def fig_three_way_hotspot(): pols = ["unified", "unified_kv_both", "unified_nixl_both", "unified_v2"] per_worker = {p: _load(f"per_worker_{p}.json") for p in pols} workers = sorted(per_worker["unified"]["per_worker_ttft_p90_s"].keys()) x = range(len(workers)) n = len(pols) width = 0.85 / n fig, ax = plt.subplots(figsize=(12, 5)) for i, p in enumerate(pols): d = per_worker[p]["per_worker_ttft_p90_s"] vals = [d[w] for w in workers] offset = (i - (n - 1) / 2) * width label = p.replace("unified_", "") if p != "unified" else "plain" ax.bar([j + offset for j in x], vals, width, label=f"{label} (hotspot={per_worker[p]['hotspot_index_ttft_p90']:.2f})", color=POLICY_COLORS[p], edgecolor="black", linewidth=0.4) short = [w.replace("http://127.0.0.1:", ":") for w in workers] ax.set_xticks(list(x)) ax.set_xticklabels(short, rotation=0, fontsize=9) ax.set_ylabel("worker TTFT p90 (s)") ax.set_title( "Per-worker TTFT p90 distribution across substrates. Mooncake (kv_both)\n" "amplifies the hot worker (hotspot 4.36); NIXL keeps it close to plain (3.67)." ) ax.legend(loc="upper left", fontsize=9) ax.grid(alpha=0.3, axis="y") fig.tight_layout() fig.savefig(OUT / "fig_three_way_hotspot.png", dpi=120) plt.close(fig) def fig_connector_substrate_attribution(): """Decomposes overhead into v1-framework cost (shared by all connectors, proxied by NIXL since it's the leanest) and Mooncake-specific cost.""" comp = _load("b3_policy_comparison.json") by = {r["policy"]: r for r in comp["rows"]} metrics = [ ("TTFT p90 (s)", "ttft_p90_s", False), ("TPOT p90 (ms)", "tpot_p90_s", True), ("E2E p90 (s)", "e2e_p90_s", False), ("hotspot index", "hotspot_index_ttft_p90", False), ] fig, axes = plt.subplots(1, 4, figsize=(15, 4)) for ax, (label, key, scale_ms) in zip(axes, metrics): plain = by["unified"][key] * (1000 if scale_ms else 1) nixl = by["unified_nixl_both"][key] * (1000 if scale_ms else 1) moon = by["unified_kv_both"][key] * (1000 if scale_ms else 1) v2 = by["unified_v2"][key] * (1000 if scale_ms else 1) framework_cost = nixl - plain # what NIXL adds = v1 framework cost mooncake_extra = moon - nixl # extra on top from Mooncake v2_branch_extra = v2 - moon # extra from PD-sep branch (Mooncake + 5 events) bottom = 0 ax.bar(["overhead"], [plain], color="#cccccc", edgecolor="black", linewidth=0.4, label=f"plain unified ({plain:.2f})") bottom += plain ax.bar(["overhead"], [framework_cost], bottom=[bottom], color="#1f77b4", edgecolor="black", linewidth=0.4, label=f"v1 framework (+{framework_cost:.2f})") bottom += framework_cost ax.bar(["overhead"], [mooncake_extra], bottom=[bottom], color="#9467bd", edgecolor="black", linewidth=0.4, label=f"Mooncake extra (+{mooncake_extra:.2f})") bottom += mooncake_extra ax.bar(["overhead"], [v2_branch_extra], bottom=[bottom], color="#d62728", edgecolor="black", linewidth=0.4, label=f"v2 PD-sep branch ({v2_branch_extra:+.2f})") ax.set_title(label) ax.legend(fontsize=8, loc="upper right") ax.grid(alpha=0.3, axis="y") ax.tick_params(axis="x", labelbottom=False) fig.suptitle( "Attribution: plain unified vs NIXL substrate vs Mooncake substrate vs v2.\n" "Blue: cost shared by any v1 connector. Purple: cost specific to Mooncake." ) fig.tight_layout() fig.savefig(OUT / "fig_connector_substrate_attribution.png", dpi=120) plt.close(fig) def main(): fig_kv_both_overhead() fig_v2_trigger_funnel() fig_v2_predicted_vs_actual() fig_three_way_hotspot() fig_connector_substrate_attribution() print(f"wrote 5 figures to {OUT}") if __name__ == "__main__": main()