Skip to content

cuda: use the 4-column GDN warp layout on all Ampere+ NVIDIA, not only GB10 (+6% prefill on Ada) - #216

Merged
bri-prism merged 2 commits into
PrismML-Eng:prismfrom
professorpalmer:cuda-gdn-cols-ampere
Sep 23, 2026
Merged

bri-prism merged 2 commits into
PrismML-Eng:prismfrom
professorpalmer:cuda-gdn-cols-ampere

Conversation

@professorpalmer

@professorpalmer professorpalmer commented Sep 19, 2026 •

Copy link
Copy Markdown

Summary

gated_delta_net_cuda's cols_per_warp = 4 layout (for S_v == 128 && !KDA) is gated to GGML_CUDA_CC_DGX_SPARK on both the host launch geometry and the device constexpr. 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 constexpr predicate:

static constexpr __host__ __device__ int gdn_cols_per_warp(int arch, int S_v, bool KDA) {
#if defined(GGML_USE_HIP) || defined(GGML_USE_MUSA)
    return 1;
#else
    return GGML_CUDA_CC_IS_NVIDIA(arch) && arch >= GGML_CUDA_CC_AMPERE && S_v == 128 && !KDA ? 4 : 1;
#endif
}
  • kernel: gdn_cols_per_warp(__CUDA_ARCH__, S_v, KDA)
  • host: GGML_CUDA_CC_IS_NVIDIA(cc) ? gdn_cols_per_warp(ggml_cuda_highest_compiled_arch(cc), S_v, KDA) : 1

Under 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):

GB10-only gate (current) Ampere+ gate (this PR)
pp512 1233 +- 10 1312 +- 13
pp2048 1220 +- 1 1297 +- 3 (+6%)
tg128 66.9 67.1 (unchanged)

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.

@bri-prism bri-prism left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@bri-prism

Copy link
Copy Markdown
Collaborator

Agent benchmark follow-up, posted at the maintainer's request.

Pinned head b16b165 against merge-base 9a9394a. Same public PTQ1 model on both arms, full GPU offload, flash attention, q4_0 K/V, batch/microbatch 512, 8 CPU threads. Three alternating baseline/candidate pairs, three repetitions per invocation, short/empty starting context.

GPU pp512 before → after, tok/s Paired change tg128 before → after, tok/s Paired change
RTX 3090 753.59 → 781.85 +3.7% 59.91 → 59.80 -0.2%
RTX 4090 1575.43 → 1595.87 +1.3% 89.62 → 89.92 +0.3%
H100 SXM 1229.12 → 1235.94 +0.6% 88.48 → 88.46 -0.0%
RTX 5090 1880.94 → 1886.53 +0.3% 119.66 → 119.15 -0.4%

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):

  • RTX 3090: +7.5% / +0.2%.
  • RTX 4090: +3.5% / -0.6%.
  • H100 SXM: +1.8% / -0.0%.
  • RTX 5090: +2.6% / -0.2%.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…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.
@professorpalmer

Copy link
Copy Markdown
Author

Revision 2 pushed (a02de73), addressing the P1.

Host and kernel now derive the column count from one constexpr __host__ __device__ predicate, gdn_cols_per_warp(arch, S_v, KDA). The kernel evaluates it on __CUDA_ARCH__; the host evaluates it on ggml_cuda_highest_compiled_arch(cc) and only when GGML_CUDA_CC_IS_NVIDIA(cc). Under GGML_USE_HIP / GGML_USE_MUSA the predicate is 1 on both sides regardless of the vendor-offset capability value, so the 8-z-block-vs-1-column launch described in the review cannot be produced. Using the highest compiled arch rather than the raw runtime cc also covers the other case you named: a binary built only for an older arch launches the geometry of the kernel that will actually run.

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.

test-backend-ops -o GATED_DELTA_NET 39/39 on the revision 2 build. No HIP hardware here; that part is by construction.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Cross-architecture GPU launch changes need final maintainer validation, particularly on untested HIP/MUSA targets.

Review effort: Balanced
Findings: 2 Low severity

Open (2)

Comment thread ggml/src/ggml-cuda/gated_delta_net.cu Outdated
Comment on lines +4 to +9
// 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed to the host/device invariant in 937c4d8.

Comment thread ggml/src/ggml-cuda/gated_delta_net.cu Outdated
Comment on lines +235 to +237
// 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed in 937c4d8. The 4070 numbers stay in the PR body.

@bri-prism

Copy link
Copy Markdown
Collaborator

Agent review follow-up: current head 937c4d8 introduces a shared dispatch predicate that selects one column on HIP/MUSA and checks the compiled NVIDIA architecture. This addresses the host/device layout mismatch raised earlier. The native Ampere-or-newer four-column path measured above is unchanged, so those measurements remain useful historical evidence, but this newer head was not runtime-tested in this campaign.

@professorpalmer

Copy link
Copy Markdown
Author

Agreed: 937c4d8 is the shared gdn_cols_per_warp predicate (HIP/MUSA → 1 on both host and device; NVIDIA uses ggml_cuda_highest_compiled_arch(cc)) plus the Copilot comment trim. The four-column Ampere+ path is the same code your Sep 19 campaign timed at b16b165.

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.

test-backend-ops -o GATED_DELTA_NET 39/39 on the revision-2 build. No HIP/MUSA hardware here; that half is by construction of the predicate. I think the Sep 19 CHANGES_REQUESTED is satisfied.

@bri-prism bri-prism left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@bri-prism
bri-prism merged commit 25092e7 into PrismML-Eng:prism Sep 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants