gqa decode: fix cube_base output-store; measure batch scaling on one SIP

Concurrency fix (completes the cube_base change): the decode kernel's
output store o_base still used the global cube_id while every load used
the user-local cube_local. For a single user (cube_base=0) they coincide,
so it was masked; a second batched user (cube_base>0) overshot its o shard
into an unmapped VA, mis-decoded to a phantom sip0.cube0.pe8 and raised a
RoutingError. Switch o_base to cube_local (one line); single-user results
are byte-identical (decode smoke/correctness pass).

Batch harness (un-skipped): create all users' tensors first, then submit
all launches deferred and drain together, so deploys don't drive an
already-submitted launch and serialize the batch. Concurrent users on
disjoint CUBE groups now overlap to within 3-5% of the single-user
latency. Swept B in {1,2,capacity} at 8K (high-B dense runs are the
expensive ones; throughput is near-linear in B).

Measured result: aggregate throughput at a full SIP rises from 0.86
(1-kv-per-cube, 2 users) to 1.41 requests/us (8-kv-per-cube, 16 users) —
the dense mapping wins throughput, the spread mapping wins latency.
S5.2 batch paragraph updated projected -> measured; add batch_scaling
figure and plot_batch_scaling.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-22 17:37:10 -07:00
parent 8c108154a7
commit 13f5cbc317
7 changed files with 132 additions and 32 deletions
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

@@ -240,26 +240,43 @@ requires lifting $M$, either by the long-context Q-tile split
(\S\ref{sec:gqa-composite}) or by batching multiple users---which is also
the lever that converts a mapping's density into throughput.
\paragraph{Latency versus batched throughput (projected).} The latency
ladder and the density trade-off pull in opposite directions once a SIP
serves more than one request. Decode users are independent---each attends
its own KV cache---and these mappings generate no cross-CUBE traffic, so a
SIP can run several users at once on disjoint CUBE groups, up to $16/C$
users: two for \textsf{1-kv-per-cube}, sixteen for \textsf{8-kv-per-cube}.
Projecting from the measured single-user latencies under perfect overlap
(an idealization---no concurrent multi-user run has been measured yet; see
\S\ref{sec:future}), aggregate throughput at a full SIP is
$\text{users}/\text{latency}$: at $8$K it rises from \SI{0.89}{}
(\textsf{1-kv-per-cube}, 2 users) to \SI{1.48}{}~requests\,/\,\si{\micro\second}
(\textsf{8-kv-per-cube}, 16 users), so the \emph{dense} mapping wins on
throughput even though it is the \emph{slowest} per request; at $64$K the
projected throughputs converge (\SI{0.17}{}--\SI{0.18}{}) because latency
there already scales as $1/C$. The design conclusion is therefore
regime-dependent: \textsf{1-kv-per-cube} is the latency-optimal choice for
single-stream or low-batch decode, while denser mappings are the
throughput-optimal choice for high-batch short-context serving. These are
\emph{projections} assuming zero cross-user interference; validating them
against a concurrent multi-user run is 2H work (\S\ref{sec:future}).
\paragraph{Latency versus batched throughput.} The latency ladder and the
density trade-off pull in opposite directions once a SIP serves more than
one request. Decode users are independent---each attends its own KV
cache---and these mappings generate no cross-CUBE traffic, so a SIP runs
several users at once on disjoint CUBE groups, up to $16/C$ users: two for
\textsf{1-kv-per-cube}, sixteen for \textsf{8-kv-per-cube}. We measure this
directly, launching $B$ concurrent users and reading aggregate latency off
the shared clock (Figure~\ref{fig:gqa-batch}). The overlap is nearly
perfect: aggregate latency stays within \SI{3}{}--\SI{5}{\percent} of the
single-user latency all the way to a full SIP (\textsf{8-kv-per-cube} at
$16$ users is \SI{11.4}{} vs.\ \SI{10.8}{\micro\second}), so that small
residual is the only cross-user interference the shared fabric adds.
Aggregate throughput at $8$K therefore rises almost linearly with $B$, from
\SI{0.86}{}~requests\,/\,\si{\micro\second} (\textsf{1-kv-per-cube}, capped
at two users) to \SI{1.41}{} (\textsf{8-kv-per-cube}, sixteen users): the
\emph{dense} mapping wins on throughput even though it is the \emph{slowest}
per request, because it packs $8\times$ more concurrent users onto the SIP.
The design conclusion is regime-dependent: \textsf{1-kv-per-cube} is the
latency-optimal choice for single-stream or low-batch decode, while denser
mappings are the throughput-optimal choice for high-batch short-context
serving. At long context the per-request latencies already scale as $1/C$,
so the same accounting predicts the throughputs converge; a measured
long-context batch sweep is 2H work (\S\ref{sec:future}).
\begin{figure}[t]
\centering
\includegraphics[width=\linewidth]{gqa_short_context/batch_scaling.png}
\caption{Batch scaling at $S_{kv}{=}8$K: aggregate throughput (requests per
\si{\micro\second}) versus the number of concurrent decode users on one SIP,
each user on a disjoint CUBE group. Each mapping scales almost linearly with
$B$ up to its SIP capacity $16/C$---\textsf{1-kv-per-cube} saturates at two
users, \textsf{8-kv-per-cube} at sixteen. The dense mapping reaches the
highest throughput; the spread mapping is latency-optimal but
capacity-limited. Concurrent users overlap to within
\SI{3}{}--\SI{5}{\percent} of the single-user latency.}
\label{fig:gqa-batch}
\end{figure}
\begin{figure}[t]
\centering