analytical-viz: roofline tab — headline step-latency + cost-per-token plots, B knob
Two new plots at the top of the Chip Roofline tab (right after the
knob row), side by side:
1. **Latency per decode step** — one forward pass's total time as B
grows. weight_fetch (flat in B), compute (linear), KV (linear),
total. The SLO / TTFT view — bigger B = longer step.
2. **Cost per token** = step latency ÷ B. weight (shrinks 1/B),
compute (flat), KV (flat), total. The efficiency / cost view —
bigger B (up to ~2·B*) = cheaper per token.
Same underlying decomposition, two divisors. Puts the classic
throughput ↔ latency tradeoff in one glance.
Also added a **B (batch size) slider** next to the existing S_kv
slider. Both plots draw a vertical marker at the current B; the
regime KPI ("memory-bound / compute-bound / kv-bound") now uses the
slider B, so you can sweep B live and watch the label flip when you
cross B*.
step_latency_curve + StepLatencyPoint added to chip_roofline; 5 new
tests cover: weight_s batch-invariant, compute_s/kv_s linear in B,
step_total == per_token_total × B (definitional), and step ==
per_token at B=1.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ from tests.analytical_visualization.chip_roofline import (
|
||||
knee_batch,
|
||||
kv_bytes_per_token,
|
||||
per_token_latency_curve,
|
||||
step_latency_curve,
|
||||
t_com,
|
||||
t_mem_long,
|
||||
t_mem_short,
|
||||
@@ -281,6 +282,59 @@ def test_utilization_drops_monotonically_with_context():
|
||||
assert b < a
|
||||
|
||||
|
||||
# ── Step latency (undivided by B) ──────────────────────────────────
|
||||
|
||||
|
||||
def test_step_weight_is_batch_invariant():
|
||||
"""step_weight = N·b/W — same for every B (loaded once per step)."""
|
||||
m = MachineParams()
|
||||
model = PRESETS["Llama 3 8B"].model
|
||||
pts = step_latency_curve(m, model, [1, 8, 64, 512], s_kv=1024)
|
||||
ws = [p.weight_s for p in pts]
|
||||
assert all(w == pytest.approx(ws[0]) for w in ws), ws
|
||||
|
||||
|
||||
def test_step_compute_linear_in_batch():
|
||||
"""step_compute = 2·N·B/C — doubling B doubles compute."""
|
||||
m = MachineParams()
|
||||
model = PRESETS["Llama 3 8B"].model
|
||||
pts = step_latency_curve(m, model, [1, 2, 8], s_kv=1024)
|
||||
assert pts[1].compute_s == pytest.approx(2 * pts[0].compute_s)
|
||||
assert pts[2].compute_s == pytest.approx(8 * pts[0].compute_s)
|
||||
|
||||
|
||||
def test_step_kv_linear_in_batch():
|
||||
"""step_kv = B · S_kv · kv_bpt / W — linear in B."""
|
||||
m = MachineParams()
|
||||
model = PRESETS["Llama 3 8B"].model
|
||||
pts = step_latency_curve(m, model, [1, 4], s_kv=1024)
|
||||
assert pts[1].kv_s == pytest.approx(4 * pts[0].kv_s)
|
||||
|
||||
|
||||
def test_step_total_equals_per_token_times_batch():
|
||||
"""The step and per-token views are just ÷ B / × B of each other."""
|
||||
m = MachineParams()
|
||||
model = PRESETS["Llama 3 8B"].model
|
||||
B_range = [1, 4, 16, 64]
|
||||
s_kv = 1024
|
||||
step_pts = step_latency_curve(m, model, B_range, s_kv=s_kv)
|
||||
tok_pts = per_token_latency_curve(m, model, B_range, s_kv=s_kv)
|
||||
for sp, tp in zip(step_pts, tok_pts):
|
||||
assert sp.total_s == pytest.approx(tp.total_s * sp.batch)
|
||||
|
||||
|
||||
def test_step_and_per_token_identical_at_b1():
|
||||
"""At B=1, step latency == per-token cost (dividing by 1)."""
|
||||
m = MachineParams()
|
||||
model = PRESETS["Llama 3 8B"].model
|
||||
sp = step_latency_curve(m, model, [1], s_kv=1024)[0]
|
||||
tp = per_token_latency_curve(m, model, [1], s_kv=1024)[0]
|
||||
assert sp.total_s == pytest.approx(tp.total_s)
|
||||
assert sp.weight_s == pytest.approx(tp.weight_s)
|
||||
assert sp.compute_s == pytest.approx(tp.compute_s)
|
||||
assert sp.kv_s == pytest.approx(tp.kv_s)
|
||||
|
||||
|
||||
# ── App wiring: tab exists on the Streamlit app ────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user