"""Final comparison of PD-Combined vs PD-Separated (Mooncake/RDMA).""" import json, statistics, os def pct(vals, q): return vals[min(int(q * len(vals)), len(vals) - 1)] if vals else 0 # Combined (16 sessions) - completed run rows_c = [json.loads(l) for l in open("outputs/v18_combined_1000req/metrics.jsonl")] ok_c = [r for r in rows_c if not r.get("error")] ttfts_c = sorted([r["ttft_s"] for r in ok_c if r.get("ttft_s")]) tpots_c = sorted([r["tpot_s"] for r in ok_c if r.get("tpot_s") and r["tpot_s"] > 0]) lats_c = sorted([r["latency_s"] for r in ok_c if r.get("latency_s")]) sc = json.load(open("outputs/v18_combined_1000req/metrics.summary.json")) # PD-Separated Mooncake (first 200 stable requests) rows_d = [json.loads(l) for l in open("outputs/v18_pd_mooncake_lowconc/metrics.jsonl")][:200] ok_d = [r for r in rows_d if not r.get("error")] ttfts_d = sorted([r["ttft_s"] for r in ok_d if r.get("ttft_s")]) tpots_d = sorted([r["tpot_s"] for r in ok_d if r.get("tpot_s") and r["tpot_s"] > 0]) lats_d = sorted([r["latency_s"] for r in ok_d if r.get("latency_s")]) sep = "=" * 70 print(sep) print(" PD-Combined vs PD-Separated (Mooncake/RDMA)") print(" vLLM 0.18.1 | Qwen3-Coder-30B-A3B | 8xH20") print(sep) header = " {:<12} {:>16} {:>16} {:>10}".format( "Metric", "Combined(TP=8)", "PD-Sep(TP=4+4)", "Delta") print(header) dash = " {:<12} {:>16} {:>16} {:>10}".format("-" * 12, "-" * 16, "-" * 16, "-" * 10) print(dash) req_c = "{}/{}".format(len(ok_c), len(rows_c)) req_d = "{}/{}".format(len(ok_d), len(rows_d)) print(" {:<12} {:>16} {:>16}".format("Requests", req_c, req_d)) data = [ ("TTFT p50", pct(ttfts_c, 0.5), pct(ttfts_d, 0.5)), ("TTFT p90", pct(ttfts_c, 0.9), pct(ttfts_d, 0.9)), ("TPOT p50", pct(tpots_c, 0.5), pct(tpots_d, 0.5)), ("TPOT p90", pct(tpots_c, 0.9), pct(tpots_d, 0.9)), ("E2E p50", pct(lats_c, 0.5), pct(lats_d, 0.5)), ("E2E p90", pct(lats_c, 0.9), pct(lats_d, 0.9)), ] for label, cv, dv in data: delta = "{:+.0f}%".format((dv / cv - 1) * 100) if cv > 0 else "N/A" print(" {:<12} {:>15.3f}s {:>15.3f}s {:>10}".format(label, cv, dv, delta)) cache_c = sc.get("prefix_cache_hit_ratio", 0) print(" {:<12} {:>15.1f}% {:>16}".format("Cache hit", cache_c * 100, "N/A")) tput_c = len(ok_c) / sc.get("wall_clock_s", 1) print(" {:<12} {:>14.2f}/s {:>16}".format("Throughput", tput_c, "~0.06/s")) print() print(sep) print(" CONCLUSIONS FOR AGENTIC WORKLOAD") print(sep) print() print(" Trace characteristics:") print(" - I/O ratio: 61.5x (strongly prefill-dominated)") print(" - 39% requests > 32k input tokens") print(" - 16% prefix block sharing across sessions") print(" - 53% prefix cache hit ratio (APC)") print() print(" PD separation findings:") delta_tpot = (pct(tpots_d, 0.5) / pct(tpots_c, 0.5) - 1) * 100 if tpots_c else 0 delta_ttft = (pct(ttfts_d, 0.5) / pct(ttfts_c, 0.5) - 1) * 100 if ttfts_c else 0 delta_e2e = (pct(lats_d, 0.5) / pct(lats_c, 0.5) - 1) * 100 if lats_c else 0 print(" 1. TPOT {:+.0f}% - decode isolation benefit is {}".format( delta_tpot, "marginal" if abs(delta_tpot) < 20 else "significant")) print(" 2. TTFT {:+.0f}% - KV transfer + TP=4 overhead dominates".format(delta_ttft)) print(" 3. E2E {:+.0f}% - net negative on single-machine".format(delta_e2e)) print(" 4. Stability: Mooncake connector crashes after ~200 reqs under load") print() print(" Recommendation:") print(" - Single-machine 8 GPU: Combined mode is better (lower TTFT, stable)") print(" - Multi-machine: PD-Sep is promising IF cross-machine latency") print(" is hidden by RDMA and prefill doesn't share GPU with decode") print(" - Key bottleneck: this workload's heavy prefill (avg 32k tokens)") print(" makes KV transfer cost non-trivial relative to prefill time") print(" - Prefill-as-a-Service (Goal 5) should focus on cross-machine") print(" KV cache sharing, not same-machine PD split")