xuan-luo/temp / docs /fixed_budget_decode_audit.md
xuan-luo's picture
|
download
raw
10.7 kB

Fixed-budget decode audit — 2026-09-11

Which implementation was tested

The existing optimized KVPath-shared is DecodeSession(..., backend="triton_grouped"): projected 128-dimensional base plus 32-dimensional per-layer delta, a fused split-score kernel, static append cache and growing CUDA graphs. It does not perform matrix absorption. inference/decode.py explicitly rejects kvpath_absorb_base=True, and supports only gqa and projected-base kvpath-shared.

The separate kvpath_absorb model uses eager PyTorch absorption and caches anchor hidden states: 4608 values/token, not the fixed 6144 budget. mla also uses eager absorption, with 448-dimensional latent + 64-dimensional RoPE, and has 6144 values/token. There is no optimized MLA session or absorbed-KVPath-shared Triton kernel in the current project-local sources.

The quoted prior-agent handover describes GQA versus MLA. Its warning about unequal backend optimization is correct; it does not identify another absorbed KVPath-shared kernel.

Setup and interpretation

  • NVIDIA A100-PG509-200 40 GiB, BF16, context 2048, batches 1/8/32/64/128.
  • GPUs 0–3 were reported idle before the tests. Full-model measurements used GPU 0; group attention prototypes used GPU 1; diagnostic variants used GPU 2; the hardware-counter attempt used GPU 3. Other users' processes were untouched.
  • GQA and KVPath-shared: exactly 98,710,016 parameters, 6144 cache values/token. MLA: 98,703,104 parameters, 6144 values/token.
  • Random initialized weights, reproducible prompt/decode IDs (seed 123). These are implementation tests, not quality tests or trained-checkpoint tests.
  • Full-model runs perform actual chunked prefill (chunk 32), then 8 growing decode steps, with 8 warmup steps. Static capacity is 2056 tokens. CUDA-event loop times exclude prefill/JIT/capture/sampling, but can include CPU submission gaps. Eager has no graphs; optimized runs use graphs.
  • Five timed rounds for the initial static/optimized sweep, seven for the final eager sweep. All rounds are preserved; variants are sequential in seeded shuffled order. This is not a randomized interleaved full-model trial, and small differences should not be overinterpreted.
  • Absolute PyTorch allocated peaks include weights and cache (graph pools for graphed variants). Reserved memory is also saved. These are not NVML totals.

Two new standalone drivers leave the original batch<=8/shared-GPU guards and production model/kernel files unchanged. The user explicitly requested batch128 on idle devices. The full-model driver caps PyTorch allocation at 16 GiB; the group prototype driver caps it at 8 GiB and checks free memory first.

Optimized full-model results

All entries below are the existing grouped Triton implementations with graphs, split size 128, at identical parameter and cache budgets. Units: ms/decode step.

Batch GQA KVPath-shared KVPath-shared/GQA
1 0.694 0.738 1.06
8 1.089 1.308 1.20
32 1.298 2.206 1.70
64 1.998 3.899 1.95
128 3.289 6.916 2.10

At batch128 GQA FlashAttention measured 3.724 ms, so the faster measured GQA control was grouped Triton. Absolute decode allocated peaks were 3305.1 MiB for GQA grouped and 3314.2 MiB for KVPath-shared grouped. Their actual static-cache allocations are equal, including unused capacity.

Sources: inference/results/audit_batch_sweep_ctx2048.json and inference/results/audit_b128_ctx2048.json. The dynamic-cache peak entries in these initial files are superseded by the corrected eager sweep below.

Like-for-like eager absorption comparison

Final corrected batch128 results, all eager + dynamic cache on GPU 0:

Model Cache values/token ms/step Absolute decode peak, MiB
GQA 6144 17.406 3413.9
KVPath-shared projected 6144 25.625 3551.8
KVPath-shared absorbed 4608 23.265 2775.3
MLA absorbed 6144 22.492 3510.7

Absorption lowers KVPath-shared cache capacity and measured peak in this implementation; it did not beat GQA in this eager run. This does not establish how equally optimized absorbed implementations would perform. Do not compare these eager times against optimized GQA as if only architecture differed.

The corrected sweep covers all five batch sizes: inference/results/audit_eager_corrected_ctx2048.json (measurement_version=2). Eager times do not grow proportionally with batch; small/medium-batch timings are visibly affected by submission overhead and run-to-run system conditions. Batch size alone does not prove that attention HBM bandwidth dominates.

Measurement correction: the initial dynamic-cache driver reset peak stats before the entire rounds loop. Its harness-only rewind can clone several cache tensors while retaining the old dictionary, inflating the recorded peak. Version 2 resets stats after each rewind, before each timed decode sequence, then takes the maximum actual decode peak. Rewind was always outside timing. Use only version 2 for dynamic-cache peak claims. The old files remain available as development records rather than silently deleting inconvenient results.

What theory can and cannot establish

For a four-layer group, per historical token, counting the required input elements with once-per-KV-head reuse within each layer:

  • GQA: 4 * 2 * 4 * 64 = 2048.
  • Current KVPath-shared: 4 * 2 * 4 * (128 + 32) = 5120.
  • Joint group scan: 2 * 4 * 128 + 4 * 2 * 4 * 32 = 2048.

These are logical input volumes, not measured DRAM bytes. L2 reuse, transactions, scratch, cache append, GEMM dispatch and kernel launch costs change the observed behavior. Likewise useful mathematical FLOPs are not the same as issued/padded tensor-core work. The current grouped kernel has two useful query rows per KV head but pads its row tile to at least 16. Grouping four layers exposes eight useful query rows for the shared base, improving that utilization in addition to shared input loads.

Nsight Compute was attempted using actual DRAM/L2 byte and throughput metrics. It failed with ERR_NVGPUCTRPERM: the current user cannot access performance counters. No GPU permissions/settings were changed to bypass this restriction. See inference/results/audit_ncu_gqa_b128.csv. No hardware bandwidth number, L2 hit rate, or definitive memory-bound classification is claimed in this audit.

The original benchmarking/bench.py computes achieved_bw from assumed bytes divided by time. Its automatic "kernel-launch bound" message is therefore not a measured bottleneck classification. It also reports incremental peaks.

A/B feasibility: implemented group-attention prototypes

Source: benchmarking/attention_audit.py. Production architecture is unchanged. The benchmark allocates identical physical KV storage for GQA and KVPath-shared: 2048 BF16 elements/token/group (1 GiB at batch128/context2048). A/B use the same base128 + delta32 tensors as current KVPath-shared, with no latent compression, unused padding cache, token omission or reduced precision.

A: group-entry queries and one joint history scan

The prototype kernel loads a base K/V tile once for four layers' available queries, adds each layer's own delta logits before its own softmax, and computes four separate outputs. It was compared against an independent FP32 explicitly concatenated attention oracle; BF16 max absolute errors in the tested batches were below 0.000465.

The proposed architecture changes follower query inputs to the group-entry hidden state. Historical delta already exists; current-token delta can still come from each sequential layer. Its current-token contribution is merged using alpha = sigmoid(current_score - history_logsumexp) and output = (1-alpha)*history_output + alpha*current_value. An independent CPU FP64 test verifies this merge identity to 1e-12 tolerance.

The kernel benchmark excludes query projection, current-token append/merge, output projection, residuals and MLP. It does not implement or time a complete modified A model. It retains the current mathematical QK/PV operation count for the supplied queries; it changes data reuse and tensor-core tile utilization.

B: one shared base retrieval plus four delta attentions

The prototype runs one base128 attention and four delta32 attentions, each with its own softmax, using existing grouped kernels. Separate-softmax outputs are checked against independent FP32 references. All cache entries remain used.

B changes model semantics: delta no longer reweights the shared base history inside the same softmax. Output fusion/gating, projections and MLP are excluded from timing; no trained model quality or complete B decoder has been tested.

Measured group-attention times

Nine randomly interleaved rounds, 20 graph replays/round, fixed history length 2048, split128. These are four-layer history-attention-only milliseconds, not full-model growing decode times.

Batch GQA four layers Current KVPath-shared four layers A joint history B base once + deltas
8 0.0817 0.1876 0.0810 0.0961
32 0.2387 0.5437 0.2333 0.2457
128 0.8559 2.0831 0.9148 0.8586

At batch128, A is ~2.28x faster than the current KVPath-shared history-attention prototype, B ~2.43x, but A is ~6.9% slower than the GQA kernel control and B approximately ties it. These establish implementation feasibility and a route to reducing KVPath-shared's current gap, not an end-to-end win over GQA. Extra per-token summaries and split reduction scratch must be included before making any A/B peak-memory claim.

Raw samples: inference/results/audit_ab_attention_ctx2048.json.

Reproduction

Run from the repository root on an explicitly selected idle GPU:

bash benchmarking/run_fixed_budget_audit.sh 0 \
  --batch 1,8,32,64,128 \
  --variants gqa:flash,gqa:triton_grouped,kvpath-shared:triton_grouped \
  --output inference/results/audit_optimized_reproduction.json

bash benchmarking/run_fixed_budget_audit.sh 0 --rounds 7 \
  --variants gqa:dynamic_eager,kvpath-shared:dynamic_eager,kvpath_absorb:dynamic_eager,mla:dynamic_eager \
  --output inference/results/audit_eager_reproduction.json

bash benchmarking/run_fixed_budget_audit.sh 1 attention \
  --output inference/results/audit_ab_reproduction.json

The user's remembered batch128 win has not been reproduced with the current sources and tested paths. Its original command/output or exact code revision is still needed to identify the discrepancy; no claim is made that the earlier observation was impossible.

Xet Storage Details

Size:
10.7 kB
·
Xet hash:
d9f384ca450d63ab805d3db763074ab4a2437329ed2e9267d79ba5cc5df9c754

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.