const $ = (id) => document.getElementById(id); const charts = {}; const pending = new Map(); let requestId = 0; let toastTimer = null; let lastResult = null; let lastArenaRows = []; let lastCapacityTrace = []; let lastTopologyRows = []; let lastDesignRows = []; const COLORS = { blue: "#79a7ff", blue2: "#5d8fe9", steel: "#92a2b5", green: "#69c99a", amber: "#dfb966", red: "#df7d89", gray: "#667384", grid: "rgba(140,155,175,.14)", }; const runtimePill = $("runtimePill"); const runtimeText = $("runtimeText"); const worker = new Worker("./worker.mjs", { type: "module" }); worker.addEventListener("message", (event) => { const data = event.data || {}; if (data.type === "ready") { runtimePill.classList.add("ready"); runtimeText.textContent = "Python runtime ready"; ["runBtn", "arenaBtn", "capacityBtn", "topologyBtn", "designBtn"].forEach((id) => { $(id).disabled = false; }); window.setTimeout(() => document.body.classList.add("runtime-ready"), 650); return; } if (data.type === "fatal") { runtimePill.classList.add("error"); runtimeText.textContent = "Runtime failed"; console.error(data.error); return; } if (!pending.has(data.id)) return; const { resolve, reject } = pending.get(data.id); pending.delete(data.id); data.error ? reject(new Error(data.error)) : resolve(data.result); }); function callPython(action, payload) { const id = ++requestId; return new Promise((resolve, reject) => { pending.set(id, { resolve, reject }); worker.postMessage({ id, action, payload }); }); } function num(id) { return Number($(id).value); } function boolSelect(id) { return $(id).value === "on"; } function configFromUI(overrides = {}) { return { model: $("model").value, accelerator: $("accelerator").value, prefill_accelerator: $("prefillAccelerator").value, decode_accelerator: $("decodeAccelerator").value, prefill_workers: num("prefillWorkers"), decode_workers: num("decodeWorkers"), interconnect_gbps: num("interconnect"), transfer_base_ms: num("transferBase"), topology: $("topology").value, scheduler: $("scheduler").value, quantization: $("quantization").value, prefix_cache_enabled: boolSelect("prefixCache"), shared_prefix_tokens: num("sharedPrefix"), prefix_reuse_fraction: num("prefixReuse"), arrival_process: $("arrival").value, request_rate_rps: num("rate"), duration_s: num("duration"), prompt_tokens_mean: num("promptMean"), prompt_tokens_cv: num("promptCv"), output_tokens_mean: num("outputMean"), output_tokens_cv: num("outputCv"), max_batch_size: num("maxBatch"), max_batch_tokens: num("maxBatchTokens"), chunk_size: num("chunkSize"), kv_block_tokens: num("kvBlock"), burst_multiplier: num("burstMultiplier"), burst_period_s: num("burstPeriod"), seed: num("seed"), slo_ttft_ms: num("sloTtft"), slo_e2e_ms: num("sloE2e"), slo_attainment_target: 0.99, ...overrides, }; } function fmt(value, digits = 1) { if (!Number.isFinite(value)) return "N/A"; return value.toLocaleString(undefined, { maximumFractionDigits: digits }); } function pct(value, digits = 1) { return `${fmt(value * 100, digits)}%`; } function escapeHtml(value) { return String(value).replace(/[&<>'"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ })[c]); } function slug(value) { return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); } function stamp() { const d = new Date(); const pad = (v) => String(v).padStart(2, "0"); return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`; } function showToast(message) { const toast = $("toast"); toast.textContent = message; toast.classList.add("show"); if (toastTimer) clearTimeout(toastTimer); toastTimer = setTimeout(() => toast.classList.remove("show"), 1800); } async function copyText(text, message) { try { await navigator.clipboard.writeText(text); showToast(message); } catch { const area = document.createElement("textarea"); area.value = text; area.style.position = "fixed"; area.style.opacity = "0"; document.body.appendChild(area); area.select(); document.execCommand("copy"); area.remove(); showToast(message); } } function csvCell(value) { const text = String(value ?? ""); return /[",\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text; } function tableText(headers, rows, separator = "\t") { return [headers, ...rows].map((row) => row.join(separator)).join("\n"); } function downloadCsv(filename, headers, rows) { const csv = [headers, ...rows].map((row) => row.map(csvCell).join(",")).join("\n"); const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); } function downloadJson(result) { const cfg = result.config || configFromUI(); const topology = cfg.topology === "disaggregated_pd" ? `pd-${cfg.prefill_workers}p-${cfg.decode_workers}d` : `colocated-${cfg.accelerator}`; const name = `inferscale-run_${slug(cfg.model)}_${slug(topology)}_${slug(cfg.scheduler)}_${stamp()}.json`; const blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); } Chart.defaults.color = "#929dab"; Chart.defaults.borderColor = COLORS.grid; Chart.defaults.font.family = getComputedStyle(document.body).fontFamily; Chart.defaults.animation.duration = 160; function commonChartOptions() { return { responsive: true, maintainAspectRatio: false }; } function lineLegend() { return { labels: { usePointStyle: true, pointStyle: "line", pointStyleWidth: 26, boxWidth: 26, boxHeight: 2, padding: 14, }, }; } function pointLegend() { return { labels: { usePointStyle: true, pointStyle: "circle", boxWidth: 8, boxHeight: 8, padding: 14, }, }; } function destroyChart(name) { if (charts[name]) { charts[name].destroy(); delete charts[name]; } } function chartFileName(card) { const cfg = configFromUI(); const chartName = card.dataset.chartName || "figure"; const topology = cfg.topology === "disaggregated_pd" ? `pd-${cfg.prefill_workers}p-${cfg.decode_workers}d-${cfg.prefill_accelerator}-${cfg.decode_accelerator}` : `colocated-${cfg.accelerator}`; return `inferscale_${slug(chartName)}_${slug(cfg.model)}_${slug(topology)}_${slug(cfg.scheduler)}_${stamp()}.png`; } function downloadChart(card) { const canvas = card.querySelector("canvas"); const chart = Chart.getChart(canvas); if (!chart) return; const exportCanvas = document.createElement("canvas"); exportCanvas.width = canvas.width; exportCanvas.height = canvas.height; const ctx = exportCanvas.getContext("2d"); ctx.fillStyle = "#0a0f15"; ctx.fillRect(0, 0, exportCanvas.width, exportCanvas.height); ctx.drawImage(canvas, 0, 0); exportCanvas.toBlob((blob) => { if (!blob) return; const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = chartFileName(card); a.click(); URL.revokeObjectURL(url); showToast("Chart PNG downloaded"); }, "image/png"); } function renderSimulation(result) { lastResult = result; $("emptyState").classList.add("hidden"); $("resultContent").classList.remove("hidden"); $("exportBtn").disabled = false; $("copyResultBtn").disabled = false; const s = result.summary; const l = result.latency; const r = result.resource; const d = result.diagnostics || {}; $("mTtft").textContent = `${fmt(l.ttft_ms.p95)} ms`; $("mE2e").textContent = `${fmt(l.e2e_ms.p95)} ms`; $("mGoodput").textContent = `${fmt(s.goodput_rps, 2)} req/s`; $("mSlo").textContent = pct(s.slo_attainment); $("mReq").textContent = `${fmt(s.request_throughput_rps, 2)} req/s`; $("mKv").textContent = `${fmt(r.peak_kv_gb, 2)} GB`; const tag = $("runState"); tag.textContent = `${s.requests_completed}/${s.requests_generated} completed`; tag.className = `tag ${s.slo_attainment >= .99 && s.requests_unfinished === 0 ? "good" : "bad"}`; $("mBottleneck").textContent = d.label || "N/A"; $("mDiagnosis").textContent = d.explanation || "No simulator diagnosis available."; $("mRecommendation").textContent = d.recommendation ? `Next check: ${d.recommendation}` : ""; const e = d.evidence || {}; const evidence = [ `topology ${r.topology || result.config.topology || "colocated"}`, `busy ${pct(e.busy_fraction ?? s.busy_fraction)}`, `KV ${pct(e.peak_kv_utilization ?? r.peak_kv_utilization)}`, `TTFT pass ${pct(e.ttft_slo_attainment ?? s.ttft_slo_attainment)}`, `E2E pass ${pct(e.e2e_slo_attainment ?? s.e2e_slo_attainment)}`, `queue p95 ${fmt(e.queue_p95_ms ?? l.queue_ms.p95)} ms`, ]; if ((r.prefix_cache_hit_rate ?? 0) > 0) evidence.push(`cache hit ${pct(r.prefix_cache_hit_rate)}`, `prefill saved ${fmt(r.prefill_tokens_saved, 0)} tok`); if (r.topology === "disaggregated_pd") evidence.push( `prefill busy ${pct(r.prefill_busy_fraction)}`, `decode busy ${pct(r.decode_busy_fraction)}`, `link busy ${pct(r.transfer_busy_fraction)}`, `transfer p95 ${fmt(r.p95_transfer_ms, 2)} ms`, ); $("mEvidence").innerHTML = evidence.map((x) => `${escapeHtml(x)}`).join(""); destroyChart("latency"); charts.latency = new Chart($("latencyChart"), { type: "bar", data: { labels: ["TTFT p50", "TTFT p95", "E2E p50", "E2E p95", "Queue p95"], datasets: [{ label: "Milliseconds", data: [l.ttft_ms.p50, l.ttft_ms.p95, l.e2e_ms.p50, l.e2e_ms.p95, l.queue_ms.p95], backgroundColor: [COLORS.steel, COLORS.blue, "#8196b1", COLORS.blue2, COLORS.amber], }], }, options: { ...commonChartOptions(), plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }, }); const timeline = result.timeline || []; const lineDatasets = [ { label: r.topology === "disaggregated_pd" ? "Prefill queue" : "Waiting", data: timeline.map((x) => ({ x: x.time_s, y: x.waiting })), borderColor: COLORS.amber, pointRadius: 0, tension: .08, yAxisID: "y" }, { label: "Decoding", data: timeline.map((x) => ({ x: x.time_s, y: x.decoding })), borderColor: COLORS.blue, pointRadius: 0, tension: .08, yAxisID: "y" }, { label: "KV GB", data: timeline.map((x) => ({ x: x.time_s, y: x.kv_used_gb })), borderColor: COLORS.green, pointRadius: 0, tension: .08, yAxisID: "y1" }, ]; if (r.topology === "disaggregated_pd") { lineDatasets.splice(1, 0, { label: "Decode queue", data: timeline.map((x) => ({ x: x.time_s, y: x.decode_ready || 0 })), borderColor: COLORS.steel, borderDash: [4, 3], pointRadius: 0, tension: .08, yAxisID: "y" }); } destroyChart("timeline"); charts.timeline = new Chart($("timelineChart"), { type: "line", data: { datasets: lineDatasets }, options: { ...commonChartOptions(), parsing: false, interaction: { mode: "nearest", intersect: false }, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", title: { display: true, text: "Virtual time (s)" } }, y: { beginAtZero: true, title: { display: true, text: "Requests" } }, y1: { beginAtZero: true, position: "right", grid: { drawOnChartArea: false }, title: { display: true, text: "KV GB" } }, }, }, }); const sample = result.requests || []; destroyChart("scatter"); charts.scatter = new Chart($("scatterChart"), { type: "scatter", data: { datasets: [{ label: "Requests", data: sample.map((x) => ({ x: x.prompt_tokens, y: x.ttft_ms })), backgroundColor: "rgba(121,167,255,.55)", pointRadius: 2.2 }] }, options: { ...commonChartOptions(), plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: "Prompt tokens" } }, y: { title: { display: true, text: "TTFT (ms)" }, beginAtZero: true } } }, }); const warnings = $("warnings"); const allWarnings = [...(result.warnings || [])]; if (result.provenance?.profile_warning) allWarnings.unshift(result.provenance.profile_warning); if (allWarnings.length) { warnings.innerHTML = allWarnings.map((w) => `
${escapeHtml(w)}
`).join(""); warnings.classList.remove("hidden"); } else { warnings.classList.add("hidden"); } } $("runBtn").addEventListener("click", async () => { const button = $("runBtn"); const state = $("runState"); button.disabled = true; button.textContent = "Running simulation..."; state.textContent = "Simulating..."; state.className = "tag neutral"; try { renderSimulation(await callPython("simulate", configFromUI())); } catch (error) { state.textContent = "Error"; state.className = "tag bad"; alert(`Simulation failed: ${error.message}`); } finally { button.textContent = "Run simulation"; button.disabled = false; } }); $("copyResultBtn").addEventListener("click", () => { if (lastResult) copyText(JSON.stringify(lastResult, null, 2), "Result JSON copied"); }); $("exportBtn").addEventListener("click", () => { if (lastResult) downloadJson(lastResult); }); function schedulerLabel(value) { return ({ static_fcfs: "Static FCFS", continuous_fcfs: "Continuous FCFS", continuous_sjf: "Continuous SJF", continuous_slo: "Continuous SLO", chunked_slo: "Chunked SLO", })[value] || value; } function arenaTableRows(rows) { return rows.map((r) => [schedulerLabel(r.scheduler), `${fmt(r.goodput_rps, 2)} req/s`, pct(r.slo_attainment), `${fmt(r.p95_ttft_ms)} ms`, `${fmt(r.p95_e2e_ms)} ms`, pct(r.peak_kv_utilization), fmt(r.unfinished, 0), r.bottleneck || "N/A"]); } function renderArena(rows) { lastArenaRows = rows; $("arenaEmpty").classList.add("hidden"); $("arenaContent").classList.remove("hidden"); $("arenaCopyBtn").disabled = false; $("arenaCsvBtn").disabled = false; $("arenaRows").innerHTML = rows.map((r, index) => `${escapeHtml(schedulerLabel(r.scheduler))}${index === 0 ? 'Best' : ""}${fmt(r.goodput_rps, 2)} req/s${fmt(r.goodput_per_accelerator, 2)} req/s/GPU${pct(r.slo_attainment)}${fmt(r.p95_ttft_ms)} ms${fmt(r.p95_e2e_ms)} ms${pct(r.peak_kv_utilization)}${fmt(r.unfinished, 0)}${escapeHtml(r.bottleneck || "N/A")}`).join(""); destroyChart("arena"); charts.arena = new Chart($("arenaChart"), { type: "bar", data: { labels: rows.map((r) => schedulerLabel(r.scheduler)), datasets: [{ label: "Goodput (req/s)", data: rows.map((r) => r.goodput_rps), backgroundColor: COLORS.blue }, { label: "Raw throughput (req/s)", data: rows.map((r) => r.request_throughput_rps), backgroundColor: "#53677f" }] }, options: { ...commonChartOptions(), scales: { y: { beginAtZero: true } } }, }); } $("arenaBtn").addEventListener("click", async () => { const button = $("arenaBtn"); button.disabled = true; button.textContent = "Comparing..."; try { renderArena((await callPython("compare", { config: configFromUI() })).rows); } catch (error) { alert(`Scheduler comparison failed: ${error.message}`); } finally { button.disabled = false; button.textContent = "Compare schedulers"; } }); const arenaHeaders = ["Scheduler", "Goodput", "SLO attainment", "p95 TTFT", "p95 E2E", "KV peak", "Unfinished", "Diagnosis"]; $("arenaCopyBtn").addEventListener("click", () => copyText(tableText(arenaHeaders, arenaTableRows(lastArenaRows)), "Scheduler table copied")); $("arenaCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_scheduler-arena_${stamp()}.csv`, arenaHeaders, arenaTableRows(lastArenaRows))); function capacityTableRows(trace) { return trace.map((r) => [ `${fmt(r.rate_rps, 2)} req/s`, r.passed ? "PASS" : "FAIL", pct(r.slo_attainment), pct(r.slo_attainment_min), pct(r.target ?? num("targetSlo")), `${pct(r.slo_attainment_min)} - ${pct(r.slo_attainment_max)}`, `${fmt(r.goodput_rps, 2)} req/s`, `${fmt(r.p95_ttft_ms)} ms`, `${fmt(r.p95_e2e_ms)} ms`, ]); } function renderCapacity(result) { lastCapacityTrace = result.trace || []; $("plannerEmpty").classList.add("hidden"); $("plannerContent").classList.remove("hidden"); $("capacityCopyBtn").disabled = false; $("capacityCsvBtn").disabled = false; $("pCapacity").textContent = `${fmt(result.capacity_rps, 2)} req/s`; $("pRecommended").textContent = `${fmt(result.recommended_rps, 2)} req/s`; $("pHeadroom").textContent = pct(result.headroom ?? num("headroom")); $("pStatus").textContent = result.status.replaceAll("_", " "); const state = $("plannerState"); state.textContent = result.status === "ok" ? "Search complete" : result.status.replaceAll("_", " "); state.className = `tag ${result.capacity_rps > 0 ? "good" : "bad"}`; $("capacityRows").innerHTML = lastCapacityTrace.map((r) => `${fmt(r.rate_rps, 2)} req/s${r.passed ? "PASS" : "FAIL"}${pct(r.slo_attainment)}${pct(r.slo_attainment_min)}${pct(r.target ?? num("targetSlo"))}${pct(r.slo_attainment_min)} - ${pct(r.slo_attainment_max)}${fmt(r.goodput_rps, 2)} req/s${fmt(r.p95_ttft_ms)} ms${fmt(r.p95_e2e_ms)} ms`).join(""); if (!lastCapacityTrace.length) return; const target = num("targetSlo"); const mean = lastCapacityTrace.map((r) => ({ x: r.rate_rps, y: r.slo_attainment })); const worst = lastCapacityTrace.map((r) => ({ x: r.rate_rps, y: r.slo_attainment_min })); const xs = lastCapacityTrace.map((r) => r.rate_rps); const minX = Math.min(...xs); const maxX = Math.max(...xs); destroyChart("capacity"); charts.capacity = new Chart($("capacityChart"), { type: "line", data: { datasets: [ { label: "Mean SLO attainment", data: mean, borderColor: COLORS.blue, backgroundColor: COLORS.blue, fill: false, tension: .06, pointRadius: 3 }, { label: "Worst repetition", data: worst, borderColor: COLORS.amber, backgroundColor: COLORS.amber, borderDash: [5, 4], fill: false, pointRadius: 2, tension: .06 }, { label: "Target", data: [{ x: minX, y: target }, { x: maxX, y: target }], borderColor: COLORS.green, backgroundColor: COLORS.green, borderDash: [6, 5], fill: false, pointRadius: 0 }, ] }, options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", title: { display: true, text: "Offered load (req/s)" }, ticks: { maxTicksLimit: 8 } }, y: { min: 0, max: 1, ticks: { callback: (v) => `${Math.round(v * 100)}%` } } } }, }); } $("capacityBtn").addEventListener("click", async () => { const button = $("capacityBtn"); const state = $("plannerState"); button.disabled = true; button.textContent = "Searching..."; state.textContent = "Running simulations..."; state.className = "tag neutral"; try { renderCapacity(await callPython("capacity", { config: configFromUI({ slo_attainment_target: num("targetSlo") }), min_rate: num("minRate"), max_rate: num("maxRate"), iterations: num("searchIter"), repetitions: num("repetitions"), headroom: num("headroom"), })); } catch (error) { state.textContent = "Error"; state.className = "tag bad"; alert(`Capacity search failed: ${error.message}`); } finally { button.disabled = false; button.textContent = "Find sustainable capacity"; } }); const capacityHeaders = ["Rate", "Pass", "Mean SLO", "Worst repetition", "Target", "SLO range", "Goodput", "p95 TTFT", "p95 E2E"]; $("capacityCopyBtn").addEventListener("click", () => copyText(tableText(capacityHeaders, capacityTableRows(lastCapacityTrace)), "Capacity table copied")); $("capacityCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_capacity-trace_${stamp()}.csv`, capacityHeaders, capacityTableRows(lastCapacityTrace))); function scenarioLabel(value) { return ({ colocated: "Colocated", colocated_cache: "Colocated + prefix cache", disaggregated_pd: "P/D disaggregated", disaggregated_pd_cache: "P/D + prefix cache", })[value] || value; } function topologyTableRows(rows) { return rows.map((r) => [ scenarioLabel(r.scenario), fmt(r.accelerator_instances, 0), `${fmt(r.goodput_rps, 2)} req/s`, `${fmt(r.goodput_per_accelerator, 2)} req/s/GPU`, pct(r.slo_attainment), `${fmt(r.p95_ttft_ms)} ms`, `${fmt(r.p95_e2e_ms)} ms`, `${fmt(r.p95_transfer_ms, 2)} ms`, pct(r.prefix_hit_rate), `${fmt(r.prefill_tokens_saved, 0)} tok`, r.bottleneck || "N/A", ]); } function renderTopology(rows) { lastTopologyRows = rows; $("topologyEmpty").classList.add("hidden"); $("topologyContent").classList.remove("hidden"); $("topologyCopyBtn").disabled = false; $("topologyCsvBtn").disabled = false; $("topologyRows").innerHTML = rows.map((r, index) => `${escapeHtml(scenarioLabel(r.scenario))}${index === 0 ? 'Best' : ""}${fmt(r.accelerator_instances, 0)}${fmt(r.goodput_rps, 2)} req/s${fmt(r.goodput_per_accelerator, 2)} req/s/GPU${pct(r.slo_attainment)}${fmt(r.p95_ttft_ms)} ms${fmt(r.p95_e2e_ms)} ms${fmt(r.p95_transfer_ms, 2)} ms${pct(r.prefix_hit_rate)}${fmt(r.prefill_tokens_saved, 0)} tok${escapeHtml(r.bottleneck || "N/A")}`).join(""); const palette = [COLORS.blue, COLORS.green, COLORS.amber, COLORS.red]; destroyChart("topology"); charts.topology = new Chart($("topologyChart"), { type: "scatter", data: { datasets: rows.map((r, i) => ({ label: scenarioLabel(r.scenario), data: [{ x: r.p95_ttft_ms, y: r.goodput_rps }], backgroundColor: palette[i % palette.length], borderColor: palette[i % palette.length], pointRadius: 6, pointHoverRadius: 8 })) }, options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput (req/s)" }, beginAtZero: true } } }, }); } $("topologyBtn").addEventListener("click", async () => { const button = $("topologyBtn"); button.disabled = true; button.textContent = "Comparing..."; try { renderTopology((await callPython("topology_compare", { config: configFromUI() })).rows); } catch (error) { alert(`Topology comparison failed: ${error.message}`); } finally { button.disabled = false; button.textContent = "Compare 4 scenarios"; } }); const topologyHeaders = ["Scenario", "GPU instances", "Goodput", "Goodput / GPU", "SLO attainment", "p95 TTFT", "p95 E2E", "p95 KV transfer", "Cache hit", "Prefill saved", "Diagnosis"]; $("topologyCopyBtn").addEventListener("click", () => copyText(tableText(topologyHeaders, topologyTableRows(lastTopologyRows)), "Topology table copied")); $("topologyCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_topology-cache-study_${stamp()}.csv`, topologyHeaders, topologyTableRows(lastTopologyRows))); function designTableRows(rows) { return rows.map((r) => [ r.label, r.pareto ? "YES" : "NO", r.efficiency_pareto ? "YES" : "NO", r.slo_pass ? "PASS" : "FAIL", fmt(r.accelerator_instances, 0), `${fmt(r.goodput_rps, 2)} req/s`, `${fmt(r.goodput_per_accelerator, 2)} req/s/GPU`, `${fmt(r.p95_ttft_ms)} ms`, `${fmt(r.p95_e2e_ms)} ms`, pct(r.peak_kv_utilization), r.bottleneck || "N/A", ]); } function renderDesign(result) { lastDesignRows = result.rows || []; $("designEmpty").classList.add("hidden"); $("designContent").classList.remove("hidden"); $("designCopyBtn").disabled = false; $("designCsvBtn").disabled = false; $("dCandidates").textContent = fmt(result.candidate_count, 0); $("dPareto").textContent = fmt(result.pareto_count, 0); $("dEfficiencyPareto").textContent = fmt(result.efficiency_pareto_count, 0); $("designRows").innerHTML = lastDesignRows.map((r) => `${escapeHtml(r.label)}${r.pareto ? 'Perf' : ""}${r.efficiency_pareto ? 'Eff' : ""}${r.pareto ? "YES" : "NO"}${r.efficiency_pareto ? "YES" : "NO"}${r.slo_pass ? "PASS" : "FAIL"}${fmt(r.accelerator_instances, 0)}${fmt(r.goodput_rps, 2)} req/s${fmt(r.goodput_per_accelerator, 2)} req/s/GPU${fmt(r.p95_ttft_ms)} ms${fmt(r.p95_e2e_ms)} ms${pct(r.peak_kv_utilization)}${escapeHtml(r.bottleneck || "N/A")}`).join(""); const passPoints = lastDesignRows.filter((r) => r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); const failPoints = lastDesignRows.filter((r) => !r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); const frontier = lastDesignRows.filter((r) => r.pareto).sort((a, b) => a.p95_ttft_ms - b.p95_ttft_ms).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); destroyChart("design"); charts.design = new Chart($("designChart"), { type: "scatter", data: { datasets: [ { label: "SLO pass", data: passPoints, backgroundColor: COLORS.green, borderColor: COLORS.green, pointRadius: 4 }, { label: "SLO fail", data: failPoints, backgroundColor: COLORS.gray, borderColor: COLORS.gray, pointRadius: 4 }, { type: "line", label: "Performance frontier", data: frontier, borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointBackgroundColor: COLORS.blue, pointRadius: 5, fill: false, tension: 0 }, ] }, options: { ...commonChartOptions(), parsing: false, plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput (req/s)" }, beginAtZero: true } } }, }); const efficiencyPass = lastDesignRows.filter((r) => r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); const efficiencyFail = lastDesignRows.filter((r) => !r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); const efficiencyFrontier = lastDesignRows.filter((r) => r.efficiency_pareto).sort((a, b) => a.p95_ttft_ms - b.p95_ttft_ms).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); destroyChart("designEfficiency"); charts.designEfficiency = new Chart($("designEfficiencyChart"), { type: "scatter", data: { datasets: [ { label: "SLO pass", data: efficiencyPass, backgroundColor: COLORS.green, borderColor: COLORS.green, pointRadius: 4 }, { label: "SLO fail", data: efficiencyFail, backgroundColor: COLORS.gray, borderColor: COLORS.gray, pointRadius: 4 }, { type: "line", label: "Efficiency frontier", data: efficiencyFrontier, borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointBackgroundColor: COLORS.amber, pointRadius: 5, fill: false, tension: 0 }, ] }, options: { ...commonChartOptions(), parsing: false, plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput / accelerator (req/s/GPU)" }, beginAtZero: true } } }, }); } $("designBtn").addEventListener("click", async () => { const button = $("designBtn"); button.disabled = true; button.textContent = "Exploring..."; try { renderDesign(await callPython("design_space", { config: configFromUI(), include_disaggregated: $("includePd").checked })); } catch (error) { alert(`Design-space exploration failed: ${error.message}`); } finally { button.disabled = false; button.textContent = "Explore design space"; } }); const designHeaders = ["Candidate", "Perf Pareto", "Efficiency Pareto", "SLO pass", "GPU instances", "Goodput", "Goodput / GPU", "p95 TTFT", "p95 E2E", "KV peak", "Diagnosis"]; $("designCopyBtn").addEventListener("click", () => copyText(tableText(designHeaders, designTableRows(lastDesignRows)), "Design table copied")); $("designCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_design-space_${stamp()}.csv`, designHeaders, designTableRows(lastDesignRows))); function syncConditionalControls() { const pd = $("topology").value === "disaggregated_pd"; $("pdControls").classList.toggle("hidden", !pd); $("colocatedAcceleratorLabel").classList.toggle("hidden", pd); if (pd && $("scheduler").value === "static_fcfs") { $("scheduler").value = "continuous_fcfs"; showToast("Static FCFS switched to Continuous FCFS for P/D topology"); } $("prefixControls").classList.toggle("hidden", !boolSelect("prefixCache")); $("burstControls").classList.toggle("hidden", $("arrival").value !== "bursty"); } $("topology").addEventListener("change", syncConditionalControls); $("prefixCache").addEventListener("change", syncConditionalControls); $("arrival").addEventListener("change", syncConditionalControls); syncConditionalControls(); for (const tab of document.querySelectorAll(".tab")) { tab.addEventListener("click", () => { document.querySelectorAll(".tab").forEach((item) => item.classList.remove("active")); document.querySelectorAll(".tab-panel").forEach((panel) => panel.classList.remove("active")); tab.classList.add("active"); $(tab.dataset.tab).classList.add("active"); setTimeout(() => Object.values(charts).forEach((chart) => chart.resize()), 20); }); } function closeExpandedChart() { const card = document.querySelector(".chart-card.chart-expanded"); if (!card) return; card.classList.remove("chart-expanded"); const button = card.querySelector(".chart-expand"); if (button) button.textContent = "Expand"; document.body.classList.remove("chart-open"); setTimeout(() => Chart.getChart(card.querySelector("canvas"))?.resize(), 20); } for (const button of document.querySelectorAll(".chart-expand")) { button.addEventListener("click", () => { const card = button.closest(".chart-card"); const wasExpanded = card.classList.contains("chart-expanded"); closeExpandedChart(); if (!wasExpanded) { card.classList.add("chart-expanded"); button.textContent = "Close"; document.body.classList.add("chart-open"); setTimeout(() => Chart.getChart(card.querySelector("canvas"))?.resize(), 20); } }); } for (const button of document.querySelectorAll(".chart-download")) { button.addEventListener("click", () => downloadChart(button.closest(".chart-card"))); } document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeExpandedChart(); });