import type { PerfPoint } from "@/lib/results"; /** * Aggregate throughput plotted against concurrency, drawn as a scope sweep. * * The x axis is logarithmic because concurrency is sampled in octaves * (1, 8, 32). On a linear axis the low-concurrency points — the ones that * describe what a single user actually feels — collapse against the origin * and the most important part of the curve becomes unreadable. */ export function ScopeTrace({ points, width = 520, height = 150, showAxis = true, }: { points: PerfPoint[]; width?: number; height?: number; showAxis?: boolean; }) { if (points.length < 2) return null; const pad = { top: 14, right: 14, bottom: showAxis ? 22 : 10, left: showAxis ? 46 : 10 }; const w = width - pad.left - pad.right; const h = height - pad.top - pad.bottom; const xs = points.map((p) => Math.log2(Math.max(1, p.concurrency))); const ys = points.map((p) => p.output_tps_total); const xMax = Math.max(...xs) || 1; const yMax = Math.max(...ys) * 1.15; const px = (i: number) => pad.left + (xs[i] / xMax) * w; const py = (v: number) => pad.top + h - (v / yMax) * h; const path = points.map((p, i) => `${i === 0 ? "M" : "L"}${px(i)},${py(p.output_tps_total)}`).join(" "); const area = `${path} L${px(points.length - 1)},${pad.top + h} L${px(0)},${pad.top + h} Z`; // Rough path length so the draw-on animation has a dash offset to work with. const traceLen = Math.round(w * 1.6); const gridLines = [0, 0.25, 0.5, 0.75, 1]; return ( {gridLines.map((g) => ( ))} {showAxis && gridLines .filter((g) => g > 0) .map((g) => ( {Math.round(yMax * g)} ))} {points.map((p, i) => ( {showAxis && ( {p.concurrency} )} ))} {showAxis && ( TOK/S AGGREGATE ↑ / CONCURRENCY → )} ); }