cuda: use the 4-column GDN warp layout on all Ampere+ NVIDIA, not only GB10 (+6% prefill on Ada) - #216
Conversation
bri-prism
left a comment
There was a problem hiding this comment.
Agent review: posted by the maintainer's coding agent at their request.
Request changes: P1 - Match host and device GDN geometry on HIP.
At ggml/src/ggml-cuda/gated_delta_net.cu:223, cc >= GGML_CUDA_CC_AMPERE is also true for AMD capability values, which include GGML_CUDA_CC_OFFSET_AMD (0x1000000). For S_v == 128 && !KDA, the host therefore selects four columns per warp. The device branch at line 47 requires __CUDA_ARCH__, so HIP still compiles one column per warp.
With four warps the host launches eight z-blocks, and the kernel processes only columns 0-31 of 128. The remaining attention/state outputs are unwritten. Please gate the host choice on NVIDIA and ensure it matches the compiled kernel architecture, not just the runtime capability.
Validated by tracing the capability constants, launch geometry and kernel column indexing. No HIP device execution was performed.
Reviewed commit: b16b165949543be90d951dc68ad7b117617264de.
|
Agent benchmark follow-up, posted at the maintainer's request. Pinned head
Selected CPU-reference backend checks passed on the compared arms. The HIP dispatch issue raised in the earlier review remains unresolved by these NVIDIA-only checks. The percentages describe these paired runs; small changes should not be interpreted as established improvements. No long-context, multi-slot serving, or end-to-end logit-parity claim is made. PQ2 control measurements (paired pp512 / tg128 changes):
|
…y GB10 (+6% prefill on Ada) The S_v == 128 GATED_DELTA_NET kernel has a 4-columns-per-warp variant that was gated to GGML_CUDA_CC_DGX_SPARK. The recurrence is serial over tokens, so prefill on a hybrid model lives or dies on this kernel's per-step efficiency, and the wider layout is a straight win on consumer Ampere/Ada as well. Host launch geometry and compiled kernel now derive the column count from one constexpr predicate, gdn_cols_per_warp(arch, S_v, KDA): the kernel evaluates it on __CUDA_ARCH__, the host on ggml_cuda_highest_compiled_arch(cc) (the arch the device will actually run, not the raw runtime capability), and only for GGML_CUDA_CC_IS_NVIDIA(cc). Under GGML_USE_HIP / GGML_USE_MUSA the predicate is 1 on both sides, so AMD capability values (which carry an offset above GGML_CUDA_CC_AMPERE) can no longer make the host launch 8 z-blocks of 4 columns against a 1-column kernel. Fixes the geometry mismatch raised in review. RTX 4070 12 GB, Bonsai 2 27B PTQ1_0: pp2048 1220 -> 1297 t/s, decode unchanged. Maintainer paired runs at the previous revision: pp512 +3.7% RTX 3090, +1.3% RTX 4090, +0.6% H100, +0.3% RTX 5090; tg128 within noise everywhere. test-backend-ops CUDA0 vs CPU: GATED_DELTA_NET 39/39 supported cases.
b16b165 to
a02de73
Compare
|
Revision 2 pushed ( Host and kernel now derive the column count from one Thanks for the paired runs; positive pp512 on all four NVIDIA parts with tg128 in the noise is the expected shape, and the PQ2_0 control moving the same way confirms it is the GDN kernel and not something PTQ1_0-specific. Body updated with the predicate and your numbers.
|
| // Columns per warp for the S_v == 128, non-KDA kernel. The host launch geometry and the compiled | ||
| // kernel must agree, so both derive it from this one predicate: the kernel passes __CUDA_ARCH__, | ||
| // the host passes the highest compiled arch the device will actually run | ||
| // (ggml_cuda_highest_compiled_arch(cc)), not the raw runtime capability. NVIDIA Ampere and newer | ||
| // take 4 columns (tuned on GB10, a straight win on consumer Ampere/Ada too); HIP and MUSA keep 1, | ||
| // whatever their capability values (which carry vendor offsets above GGML_CUDA_CC_AMPERE) say. |
| // The recurrence is serial over tokens, so prefill lives or dies on this kernel's per-step | ||
| // efficiency (RTX 4070, Bonsai 2 27B: pp2048 1220 -> 1297 t/s, decode unchanged). Must match | ||
| // the kernel's compile-time choice: same predicate, fed the arch that was compiled for this cc. |
There was a problem hiding this comment.
Trimmed in 937c4d8. The 4070 numbers stay in the PR body.
|
Agent review follow-up: current head |
|
Agreed: I am treating those numbers as still valid for NVIDIA, with the same caution you wrote — this head was not re-run. For the record, the 4070 receipt that belongs with that campaign (revision-2 binary, #214 already in, four runs) is still 1233 → 1312 pp512 (+6.4%) and 1220 → 1297 pp2048; tg128 66.9 → 67.1. That is the consumer-Ada end of the same curve you reported (+3.7% 3090, +1.3% 4090, +0.6% H100, +0.3% 5090), and the PQ2_0 control moving with it is what identifies the GDN kernel rather than a PTQ1_0 interaction.
|
bri-prism
left a comment
There was a problem hiding this comment.
Agent review: posted by the maintainer's coding agent at their request.
Approving at 937c4d8. This resolves the earlier request for changes.
The shared gdn_cols_per_warp predicate closes the host/device mismatch. The kernel evaluates it on __CUDA_ARCH__ and the host on ggml_cuda_highest_compiled_arch(cc), and both return 1 under HIP and MUSA, so the launch geometry now matches the compiled kernel on every backend. The only change from a02de73 to 937c4d8 is comment text, so your 39/39 GATED_DELTA_NET run on a02de73 still covers this head.
Method note: source reading at 937c4d8. I did not build or run it, and the HIP/MUSA half is correct by construction rather than by a device run.

Summary
gated_delta_net_cuda'scols_per_warp = 4layout (forS_v == 128 && !KDA) is gated toGGML_CUDA_CC_DGX_SPARKon both the host launch geometry and the deviceconstexpr. The kernel body uses nothing GB10-specific (warp shuffles, plain loads), and because the recurrence is serial over tokens, prefill throughput depends directly on this kernel's per-step efficiency. Widening the gate to NVIDIA Ampere and newer is a straight win on consumer Ada, and the maintainer's paired runs show it is positive on 3090/4090/H100/5090 as well.Revision 2 addresses the review P1 (host/device geometry mismatch on HIP). Host and kernel now derive the column count from one
constexprpredicate:gdn_cols_per_warp(__CUDA_ARCH__, S_v, KDA)GGML_CUDA_CC_IS_NVIDIA(cc) ? gdn_cols_per_warp(ggml_cuda_highest_compiled_arch(cc), S_v, KDA) : 1Under HIP/MUSA both sides are 1 regardless of the vendor-offset capability value, so the host can no longer launch 8 z-blocks of 4 columns against a 1-column kernel. On CUDA the host uses
ggml_cuda_highest_compiled_arch(cc)rather than the raw runtime cc, so a fat binary built only for an older arch (e.g. sm_75 PTX running on Ada) launches the geometry of the kernel that will actually execute, which is the second half of the review's request.Measurements
RTX 4070 (sm_89), Ternary-Bonsai-2-27B-PTQ1_0,
llama-bench -fa on, 4 runs each, otherwise identical binary (includes #214):Maintainer paired runs at revision 1 (same predicate result on NVIDIA, so unchanged by revision 2): pp512 +3.7% RTX 3090, +1.3% RTX 4090, +0.6% H100 SXM, +0.3% RTX 5090; tg128 within noise on all four; PQ2_0 control +7.5% / +3.5% / +1.8% / +2.6%. The Ada gain is larger than the datacenter gain because the 4070 is the card where this kernel is the largest share of prefill.
Correctness
test-backend-ops -b CUDA0 -o GATED_DELTA_NET: 39/39 supported cases vs CPU, revision 2 build. Greedy generation identical.Notes
Turing (cc 750) keeps
cols_per_warp = 1. Hopper and Blackwell take the 4-column path, which the paired runs above show is neutral-to-positive there. No HIP hardware here to execute on; the HIP fix is by construction (both sides compile to 1) rather than by measurement.