Repository navigation
Conversation
On sm_70 qsa_prompt_attn_batch refused the device and the prompt path fell back to the decode kernel (FP32 FMAs, one query at a time): 19-25% of the GPU time of a 32K prompt on a V100. prompt_attn_v70_kernel is the v1 kernel's structure with the two matrix products on m8n8k4 (FP16 in, FP32 accumulate): q.k with one dim group (64 dims, = one int8 scale group) per warp and 8 cells per quad-pair, p.v with 16 dims per quad-pair. q and p keep their hi + lo FP16 split, the four dim groups' partials are added in a fixed order (deterministic). Int8, FP16 and K8V4 KV are handled; Q4_0 KV (mode 4, sm_80+ only upstream) keeps the old kernel. The fragment layout was measured on a V100 and matches turbomind's SM70_MMA_884. qsa_prompt_attn_parity (V100): error against FP64 ~5e-6 (output scale ~3.6), int8 3.0x and fp16 1.3x faster than the FP32 kernel per 2048-query chunk. The test now skips its Q4_0 cases below sm_80 instead of exiting 2 there. End to end, 32K prompt, one V100, greedy output identical: 26 s -> 22 s on current main (STRATA_PROMPT_ATTN_OLD=1 is the old path). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The experimental sm_70 build (STRATA_EXPERIMENTAL_SM60, Niko1221#236) had no page. NVIDIA_V100.md says how to build it, which kernel each part of the prompt path uses on Volta and what the prompt attention kernel was measured at. bench/results/2026-10-03-v100-prompt-attn: prompt speed with and without the kernel (7K / 28K / 114K tokens), the nsys share of the attention before and after, the parity numbers, needle recall (15 of 15 with either path) and the greedy-output comparison. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
i tried it on top of #540 on a V100 (IQ2_XS, sm_70, CUDA 12.9), with only the kernel commit (ee0843d) cherry-picked onto my branch, which is upstream 0.1.37 plus the #540 commits prefill, one run each:
so it stacks with #540. #540's attention commit changes qsa_decode_attn.cu, the kernel this PR bypasses during prefill, so the two don't overlap in code. that fits your +15-18% over the old path: #540's attention commit is worth about +7% on its own, and 1.07 x 1.09 is roughly 1.17 you mention you did not run a teacher-forced comparison, so for what it is worth, first-window logits against the same build without this kernel:
that is the same size as the FP16 rounding in the BF16-via-FP16 change, so it doesn't look like a bug. if you have a KL or perplexity number on a longer prompt i'd like to see it, mine is one position per prompt and heavy-tailed caveats: single runs, V100 only, i did not run ctest on this kernel separately: the BF16 half of #540 (BF16 GEMMs through FP16 tensor cores, same STRATA_BF16_VIA_F16 toggle) is the same change as #593, so i left a note there. this PR itself doesn't overlap with either |
…ed KL, controls) Old path against new kernel on 8.5K / 33K / 99K-token chats, 617 scored positions each, with two controls that change only the rounding order (BF16 GEMMs without the FP16 detour, another prefill chunk), reruns of both paths (bit-identical), and the kernel's own error on the model's data (relative 4.4e-7 mean, 4.8e-6 worst row over ~5 M rows). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Thanks for testing this. I measured the teacher-forced comparison on longer prompts, with the project's method ( Setup: a chat whose first message (8.5k / 33k / 99k tokens of this repository's docs and code) is read by the batched prompt path, then a ~600-token last message that goes through the verify windows and is scored (617 positions per prompt). One V100, UD-IQ4_XS, old path =
So the model amplifies small numeric differences a lot (heavy-tailed: the median KL at 8.5k is 7e-4, p99 0.35), and both controls, which change nothing but the rounding order, move the distribution as much as the kernel does. At 99k the kernel is somewhat above both controls; that is one document and 617 positions, so I can neither confirm nor exclude a real effect there. Directly on the model's data, with a temporary check that ran the decode kernel beside the new one on the same inputs (every 8th query batch, all 12 QSA layers, ~5 M (query, head) rows, positions up to 98k): ||new - old|| / ||old|| per row is 4.4e-7 on average, 4.8e-6 in the worst row, max absolute difference 4e-5 against values up to 31, with no trend over layers or context length. That is FP32 rounding level. Caveats: V100 only, one run per setting (the reruns are bit-identical), the check code is not part of the PR. The table and the method are now in |
…221#593 was closed in its favor) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
that row-level check pretty much settles it for me, only thing i noticed is 99k sitting a bit above the controls. looks good to me! |
…/lo chains split The kernel is Klaus Friedel's (fks), from his open PR Niko1221#600 to Niko1221/Strata (Niko1221#600), commit ee0843d: fks@ee0843d Thanks to him for it. Taken as written, with one change: the hi and lo halves of the split q (in q.k) and p (in p.v) accumulate in their own m8n8k4 chains, added in FP32 at the end. In one chain Volta's truncating accumulation dropped most of the lo half: error vs FP64 at 32K context 5.1e-6 -> 2.3e-6 (the FP32 per-query kernel: 2.4e-6; P4's wmma kernel: 6.6e-6), 12.2 -> 12.4 ms per 2,048-query chunk (wmma 14.2). A fresh accumulator per k-step: 2.45e-6 but 15.5 ms. Opt-in, STRATA_ATTN_M884=1, int8 KV (ahead of STRATA_ATTN_WMMA, which stays the fallback). Teacher-forced vs the reference, against the wmma kernel: 32K KL 0.0232 -> 0.0220 (max 1.80 -> 0.58, top-1 93.3 -> 95.3%), 2K 0.0144 -> 0.0154 (top-1 95.2 -> 96.3%). As submitted (one chain) it was 0.0240 / 0.0168. Prompts, 4 GPUs, launch config, two rounds: 7.8K 3,912 -> 3,992, 18K 5,450 -> 5,599, 65K 7,001 -> 7,102, 123K 7,240 -> 7,323 tok/s. Co-Authored-By: Klaus Friedel <fks@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZHWKWn7UYZV64KwpGrWuN
…perimental build only (PR #600, #395 follow-up) STRATA_EXPERIMENTAL_SM60 now reaches every target (add_compile_definitions). prompt_attn_v70_kernel with its launcher, and Gemm::bf16's fp32 path for Pascal, exist only under it: the ready-made engine (sm_75+) loads no extra kernels for them, while the experimental build's sm_60/61/70 cards get them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e; --cuda 12|13 (#395 #627 #540 #600 #601) CUDA 13 dropped Pascal and Volta. A model whose GPUs include one (its oldest card decides: one engine runs per model) now runs the experimental CUDA 12 engine in engine-cuda12/ - the ready-made strata-windows-x64-cuda12.zip with NVIDIA's CUDA 12 pip libraries, or compiled here with a CUDA 12.x toolkit (Linux; STRATA_NVCC picks one, #601) - and setup says why. engine/ and every model on it stay as they were. - the cards are an opt-in: naming one (--gpu / --gpus), --cuda 12, STRATA_EXPERIMENTAL_SM60=1, or a PC with no newer NVIDIA card and no supported AMD card; a PC with a newer card keeps recommending it - --cuda 12|13 (or STRATA_CUDA) overrides the choice and is kept, with a warning where it cannot work (recommend, never force); --cuda 12 also runs with a driver older than 580 (525+), which the stop now mentions - at a start, a Pascal / Volta card added to a CUDA 13 model moves that model's config to the CUDA 12 engine - updates look at both engine folders; a compiled CUDA 12 engine builds in build-cuda12/ - an RTX 50 card in a CUDA 12 engine is a warning (CUDA 12.8+ builds it; #220 crashed with 12.8), no longer a stop The setup golden test (every recommended config on five PCs) is unchanged; tools/test_setup_older_gpus.py covers the rest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ine and how to opt in docs/OLDER_GPUS.md: Pascal / Volta through the CUDA 12 engine (#395 #600 #540 #655), RTX 20's STRATA_BF16_TC opt-in, AMD gfx1031 (#524), gfx1012 (#442) and gfx906 (#638, #677) with the reporters' numbers; README, AI_SETUP, AMD_HIP and NVIDIA_V100 point to it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged for the next release, with your commits and docs/NVIDIA_V100.md. The kernel and its launcher are compiled into the experimental build only (-DSTRATA_EXPERIMENTAL_SM60=ON), so the ready-made engine loads no extra kernels. Thanks for the teacher-forced comparison with controls. |
217 upstream commits (0.1.38 -> 0.1.39: Niko1221#646 decode kernels and zero-doorbell verify graph, Niko1221#603 long-context top-k, Niko1221#583 byte-budget ring, Niko1221#598 pipelined layer split, Niko1221#578 helper GPUs, Niko1221#663, Niko1221#540, Niko1221#600 in the experimental build, batch slots Niko1221#465, the SYCL port, older-CPU builds, ...). 23 files conflicted (~70 hunks); every hunk was resolved by hand and checked against both sides. Resolution rules: - The prompt path's stage overlap: upstream's Niko1221#598 (equivalent to our e85f7ee; run() drains the whole chain on every exit), with our release_inherited_pin in the stage threads and our early ids copy for the host grouping (a5a3254) kept. - STRATA_FP16=load's form-aware weights kept wherever upstream's new code assumed BF16: the shared-expert gate (sigmoid+scale fused as upstream), GDN alpha/beta (our kernels; upstream's new gdn_step_commit_kernel added), the hc v3 kernels (upstream's EXACT_T with our dot8w). - verify.cpp: upstream's all-resident fast path, plan copy and Niko1221#578 rows, with our 2-GPU fetch overlap and partner fetch back in the non-resident branch; mapped buffers stay portable (the partner reads them). - Volta prompt attention: upstream's dispatch (the m8n8k4 kernel on sm_70 in this build) with our split hi/lo accumulator chains inside it; the wmma kernel stays opt-in (STRATA_ATTN_WMMA=1 without STRATA_ATTN_M884). - qsa_decode_attn.cu: upstream's (our adf56cc imported the same PR Niko1221#540; upstream's is it plus review fixes, bit-exact). - POWER / aarch64: upstream's new x86 intrinsics in verify.cpp through the portable strata_store_fence / strata_cpu_pause; its AVX2 start-up checks and log line behind x86 guards; stubs for the new CPU probes; the router lookahead keeps portable.cpp's VSX dot on non-x86 (upstream's dispatch would have sent POWER to its scalar fallback); the pool's ThreadAffinity (any CPU count) with our note_host_pin; SMT2 selection with Niko1221#642's sort. - setup.py / tests: both sides (CUDA 12 engine, ISA floor, ARM / non-x86). Checked: - every line either side added since 0.1.38 is in the merge or replaced on purpose (script over all 45 files both sides changed); - all parity tests pass (iq, native_expert on the real shards, mmvq_multi, gr, gdn, gdn_rec, qsa, qsa_topk, elementwise, router_dot, shared_expert, kv_*, sampler, rope, s2/s_gemv, gemm_bf16, ...); - teacher-forced 2K and 32K logs byte-identical to the pre-merge build (4 GPUs, launch config); - 4 GPUs: decode text identical, same windows and acceptance, 28.25 -> 26.9 ms/window (~86 -> ~90 tok/s); prompts 7.8K 3,992 -> 4,038, 65K 7,102 -> 7,172, 123K 7,323 -> 7,334 tok/s; --prefill-ring 320 applies; - 2 GPUs: decode texts identical to the pre-merge binary on three prompts, decode 61.5-71.0 -> 63.5-72.7 tok/s, 65K prompt 3,975 -> 4,083. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZHWKWn7UYZV64KwpGrWuN
…perimental build only (PR Niko1221#600, Niko1221#395 follow-up) STRATA_EXPERIMENTAL_SM60 now reaches every target (add_compile_definitions). prompt_attn_v70_kernel with its launcher, and Gemm::bf16's fp32 path for Pascal, exist only under it: the ready-made engine (sm_75+) loads no extra kernels for them, while the experimental build's sm_60/61/70 cards get them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…perimental build only (PR #600, #395 follow-up) STRATA_EXPERIMENTAL_SM60 now reaches every target (add_compile_definitions). prompt_attn_v70_kernel with its launcher, and Gemm::bf16's fp32 path for Pascal, exist only under it: the ready-made engine (sm_75+) loads no extra kernels for them, while the experimental build's sm_60/61/70 cards get them.
…e; --cuda 12|13 (#395 #627 #540 #600 #601) CUDA 13 dropped Pascal and Volta. A model whose GPUs include one (its oldest card decides: one engine runs per model) now runs the experimental CUDA 12 engine in engine-cuda12/ - the ready-made strata-windows-x64-cuda12.zip with NVIDIA's CUDA 12 pip libraries, or compiled here with a CUDA 12.x toolkit (Linux; STRATA_NVCC picks one, #601) - and setup says why. engine/ and every model on it stay as they were. - the cards are an opt-in: naming one (--gpu / --gpus), --cuda 12, STRATA_EXPERIMENTAL_SM60=1, or a PC with no newer NVIDIA card and no supported AMD card; a PC with a newer card keeps recommending it - --cuda 12|13 (or STRATA_CUDA) overrides the choice and is kept, with a warning where it cannot work (recommend, never force); --cuda 12 also runs with a driver older than 580 (525+), which the stop now mentions - at a start, a Pascal / Volta card added to a CUDA 13 model moves that model's config to the CUDA 12 engine - updates look at both engine folders; a compiled CUDA 12 engine builds in build-cuda12/ - an RTX 50 card in a CUDA 12 engine is a warning (CUDA 12.8+ builds it; #220 crashed with 12.8), no longer a stop The setup golden test (every recommended config on five PCs) is unchanged; tools/test_setup_older_gpus.py covers the rest.
Port of oldlocal commit 4672cf9 ("kernels (sm_70): the prompt attention's latency pipeline - 2.2x over the FP32 kernel at 32K") onto the Niko1221#600 prompt_attn_v70_kernel, the m8n8k4 Volta kernel now inline in qsa_prompt_attn.cu. Still gated on STRATA_EXPERIMENTAL_SM60, so the ready-made engine keeps exactly the code it had. Two of the commit's three changes port; the third (splitting the dependent WMMA chains into f0/f1 and fh/fl) is already inherent to the mma884 kernel - its scores run two tg[rb] chains and its p.v four tmp[rb][nt] chains - so porting it would only change the summation order. The two ported, measured on a V100-SXM2-16GB (CUDA 12.8): * the staging loop issues every global load of a thread before any of its stores (2-4 in flight), instead of paying the HBM latency per piece; * the next chunk's ids/page-table lookups double-buffer (row[2][CH]) and run just before the previous p.v, where their latency hides. The original commit measured, on the Niko1221#371 kernel, 2,048 queries of a 32K selection: 34.4 -> 15.8 ms (2.18x) int8, 33.5 -> 15.4 ms (2.17x) fp16 KV, 35.6 -> 20.3 ms (1.76x) K8V4 (its q4_0 V dequantizes to FP16 at staging, the sm_75/80 kernel's same trade). This port, same 2,048 queries of a 32K selection (qsa_prompt_attn_sm70_bench, which also gained the K8V4 pools): the Volta kernel goes int8 12.11 -> 10.25 ms, fp16 KV 25.70 -> 17.39 ms, K8V4 25.71 -> 24.12 ms, so against the FP32 decode kernel int8 rises 1.73x -> 2.05x and fp16 0.80x -> 1.18x (K8V4, 0.85x -> 0.91x, keeps main's Niko1221#600 dequant-to-FP16 trade). qsa_prompt_attn_parity passes all four cases. An A/B against the unpatched kernel shows the kernel's output is unchanged - every FP64 and new-vs-old figure is identical before and after (int8 5.08e-6, fp16 5.29e-6 vs FP64, within the 4x gate). (cherry picked from commit 54748a5)
…perimental build only (PR Niko1221#600, Niko1221#395 follow-up) STRATA_EXPERIMENTAL_SM60 now reaches every target (add_compile_definitions). prompt_attn_v70_kernel with its launcher, and Gemm::bf16's fp32 path for Pascal, exist only under it: the ready-made engine (sm_75+) loads no extra kernels for them, while the experimental build's sm_60/61/70 cards get them.
…e; --cuda 12|13 (Niko1221#395 Niko1221#627 Niko1221#540 Niko1221#600 Niko1221#601) CUDA 13 dropped Pascal and Volta. A model whose GPUs include one (its oldest card decides: one engine runs per model) now runs the experimental CUDA 12 engine in engine-cuda12/ - the ready-made strata-windows-x64-cuda12.zip with NVIDIA's CUDA 12 pip libraries, or compiled here with a CUDA 12.x toolkit (Linux; STRATA_NVCC picks one, Niko1221#601) - and setup says why. engine/ and every model on it stay as they were. - the cards are an opt-in: naming one (--gpu / --gpus), --cuda 12, STRATA_EXPERIMENTAL_SM60=1, or a PC with no newer NVIDIA card and no supported AMD card; a PC with a newer card keeps recommending it - --cuda 12|13 (or STRATA_CUDA) overrides the choice and is kept, with a warning where it cannot work (recommend, never force); --cuda 12 also runs with a driver older than 580 (525+), which the stop now mentions - at a start, a Pascal / Volta card added to a CUDA 13 model moves that model's config to the CUDA 12 engine - updates look at both engine folders; a compiled CUDA 12 engine builds in build-cuda12/ - an RTX 50 card in a CUDA 12 engine is a warning (CUDA 12.8+ builds it; Niko1221#220 crashed with 12.8), no longer a stop The setup golden test (every recommended config on five PCs) is unchanged; tools/test_setup_older_gpus.py covers the rest.
…ine and how to opt in docs/OLDER_GPUS.md: Pascal / Volta through the CUDA 12 engine (Niko1221#395 Niko1221#600 Niko1221#540 Niko1221#655), RTX 20's STRATA_BF16_TC opt-in, AMD gfx1031 (Niko1221#524), gfx1012 (Niko1221#442) and gfx906 (Niko1221#638, Niko1221#677) with the reporters' numbers; README, AI_SETUP, AMD_HIP and NVIDIA_V100 point to it.
…perimental build only (PR Niko1221#600, Niko1221#395 follow-up) STRATA_EXPERIMENTAL_SM60 now reaches every target (add_compile_definitions). prompt_attn_v70_kernel with its launcher, and Gemm::bf16's fp32 path for Pascal, exist only under it: the ready-made engine (sm_75+) loads no extra kernels for them, while the experimental build's sm_60/61/70 cards get them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e; --cuda 12|13 (Niko1221#395 Niko1221#627 Niko1221#540 Niko1221#600 Niko1221#601) CUDA 13 dropped Pascal and Volta. A model whose GPUs include one (its oldest card decides: one engine runs per model) now runs the experimental CUDA 12 engine in engine-cuda12/ - the ready-made strata-windows-x64-cuda12.zip with NVIDIA's CUDA 12 pip libraries, or compiled here with a CUDA 12.x toolkit (Linux; STRATA_NVCC picks one, Niko1221#601) - and setup says why. engine/ and every model on it stay as they were. - the cards are an opt-in: naming one (--gpu / --gpus), --cuda 12, STRATA_EXPERIMENTAL_SM60=1, or a PC with no newer NVIDIA card and no supported AMD card; a PC with a newer card keeps recommending it - --cuda 12|13 (or STRATA_CUDA) overrides the choice and is kept, with a warning where it cannot work (recommend, never force); --cuda 12 also runs with a driver older than 580 (525+), which the stop now mentions - at a start, a Pascal / Volta card added to a CUDA 13 model moves that model's config to the CUDA 12 engine - updates look at both engine folders; a compiled CUDA 12 engine builds in build-cuda12/ - an RTX 50 card in a CUDA 12 engine is a warning (CUDA 12.8+ builds it; Niko1221#220 crashed with 12.8), no longer a stop The setup golden test (every recommended config on five PCs) is unchanged; tools/test_setup_older_gpus.py covers the rest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ine and how to opt in docs/OLDER_GPUS.md: Pascal / Volta through the CUDA 12 engine (Niko1221#395 Niko1221#600 Niko1221#540 Niko1221#655), RTX 20's STRATA_BF16_TC opt-in, AMD gfx1031 (Niko1221#524), gfx1012 (Niko1221#442) and gfx906 (Niko1221#638, Niko1221#677) with the reporters' numbers; README, AI_SETUP, AMD_HIP and NVIDIA_V100 point to it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ccumulator chains Follow-up to #600 (fks's mma.m8n8k4 prompt attention for sm_70). The kernel splits q (for q.k) and p (for p.v) into FP16 hi + lo halves and runs both products into the same accumulator. Volta's tensor cores don't keep full FP32 precision while they accumulate, so once the hi products are in the accumulator most of the much smaller lo contribution gets truncated away, and the split buys less than it should. Giving the lo products their own chain and adding the two in FP32 at the end fixes that. qsa_prompt_attn_parity, one V100-SXM2-16GB, 2,048 queries, error vs the FP64 reference (the FP32 kernel in brackets): ctx 8,192 5.88e-6 -> 3.02e-6 (2.14e-6) 11.94 -> 12.20 ms ctx 32,768 5.08e-6 -> 2.33e-6 (2.37e-6) 12.20 -> 12.38 ms ctx 131,072 5.35e-6 -> 2.75e-6 (1.81e-6) 12.34 -> 12.51 ms Registers unchanged (166 / 226 / 163 for modes 0 / 1 / 3), no spills. docs/NVIDIA_V100.md updated for the new error level.
…ccumulator chains Follow-up to Niko1221#600 (fks's mma.m8n8k4 prompt attention for sm_70). The kernel splits q (for q.k) and p (for p.v) into FP16 hi + lo halves and runs both products into the same accumulator. Volta's tensor cores don't keep full FP32 precision while they accumulate, so once the hi products are in the accumulator most of the much smaller lo contribution gets truncated away, and the split buys less than it should. Giving the lo products their own chain and adding the two in FP32 at the end fixes that. qsa_prompt_attn_parity, one V100-SXM2-16GB, 2,048 queries, error vs the FP64 reference (the FP32 kernel in brackets): ctx 8,192 5.88e-6 -> 3.02e-6 (2.14e-6) 11.94 -> 12.20 ms ctx 32,768 5.08e-6 -> 2.33e-6 (2.37e-6) 12.20 -> 12.38 ms ctx 131,072 5.35e-6 -> 2.75e-6 (1.81e-6) 12.34 -> 12.51 ms Registers unchanged (166 / 226 / 163 for modes 0 / 1 / 3), no spills. docs/NVIDIA_V100.md updated for the new error level.
Port of oldlocal commit 4672cf9 ("kernels (sm_70): the prompt attention's latency pipeline - 2.2x over the FP32 kernel at 32K") onto the Niko1221#600 prompt_attn_v70_kernel, the m8n8k4 Volta kernel now inline in qsa_prompt_attn.cu. Still gated on STRATA_EXPERIMENTAL_SM60, so the ready-made engine keeps exactly the code it had. Two of the commit's three changes port; the third (splitting the dependent WMMA chains into f0/f1 and fh/fl) is already inherent to the mma884 kernel - its scores run two tg[rb] chains and its p.v four tmp[rb][nt] chains - so porting it would only change the summation order. The two ported, measured on a V100-SXM2-16GB (CUDA 12.8): * the staging loop issues every global load of a thread before any of its stores (2-4 in flight), instead of paying the HBM latency per piece; * the next chunk's ids/page-table lookups double-buffer (row[2][CH]) and run just before the previous p.v, where their latency hides. The original commit measured, on the Niko1221#371 kernel, 2,048 queries of a 32K selection: 34.4 -> 15.8 ms (2.18x) int8, 33.5 -> 15.4 ms (2.17x) fp16 KV, 35.6 -> 20.3 ms (1.76x) K8V4 (its q4_0 V dequantizes to FP16 at staging, the sm_75/80 kernel's same trade). This port, same 2,048 queries of a 32K selection (qsa_prompt_attn_sm70_bench, which also gained the K8V4 pools): the Volta kernel goes int8 12.11 -> 10.25 ms, fp16 KV 25.70 -> 17.39 ms, K8V4 25.71 -> 24.12 ms, so against the FP32 decode kernel int8 rises 1.73x -> 2.05x and fp16 0.80x -> 1.18x (K8V4, 0.85x -> 0.91x, keeps main's Niko1221#600 dequant-to-FP16 trade). qsa_prompt_attn_parity passes all four cases. An A/B against the unpatched kernel shows the kernel's output is unchanged - every FP64 and new-vs-old figure is identical before and after (int8 5.08e-6, fp16 5.29e-6 vs FP64, within the 4x gate). (cherry picked from commit 54748a5)
On sm_70
qsa_prompt_attn_batchrefuses the device (#371), so the prompt path runs the QSA attention on the decode kernel, one query at a time with FP32 FMAs: 19.2% of the GPU time of a 28,650-token prompt on a V100 (nsys, 0.1.35,attn_chunk_kernel<1>).prompt_attn_v70_kernelis the v1 kernel's structure with the two matrix products onmma.sync.m8n8k4(FP16 in, FP32 accumulate): q.k with one 64-dim group (= one int8 scale group) per warp and 8 cells per quad-pair, p.v with 16 dims per quad-pair. q and p keep their hi + lo FP16 split, and the four dim groups' partials are added in a fixed order (deterministic). Int8, FP16 and K8V4 KV are handled; Q4_0 KV (mode 4, sm_80+ only) keeps the old kernel. The m8n8k4 fragment layout was measured on a V100 and agrees with turbomind'sSM70_MMA_884. It is used for compute capability 7.0 - 7.4;STRATA_PROMPT_ATTN_OLD=1is the old path.Accuracy (
qsa_prompt_attn_parity, synthetic, V100, 32K context, 2,048 queries per chunk, output scale ~3.6):The 1,500- and 2,100-token contexts also pass. The test now skips its Q4_0 cases below sm_80 instead of exiting 2 there (the dispatcher keeps the old kernel for them).
Prompt speed (one V100-PCIE-32GB, i9-7960X, CUDA 12.8,
origin/mainat99f3dbd, UnslothUD-IQ4_XS, int8 KV; cold server, random-word prompts, greedy; same binary,STRATA_PROMPT_ATTN_OLD=1against the default):Decode is unaffected (it does not use this path).
Output checks
tools/needle_bench.py, 8k / 32k / 128k at five depths, a fresh server per configuration): 15 of 15 found with the old path, 15 of 15 with the new kernel.Not covered: only the Unsloth IQ4_XS pack was run (not the GSQ-RCO quants); one request on one card. Method, profile and raw numbers:
bench/results/2026-10-03-v100-prompt-attn/; build and kernel overview:docs/NVIDIA_V100.md(both in this PR). Independent of the BF16-via-FP16 change in #540: different code files, and it stacks with it (a user measured +9% on top of #540)Written and measured with Claude Sonnet 5.5.