CUDA: dispatch the chunked gated_delta_net in CU mode when it fills the device - #79
CUDA: dispatch the chunked gated_delta_net in CU mode when it fills the device#79roberteg16 wants to merge 1 commit into
Conversation
|
This is the first time where I see CU-mode have a non-null effect. |
Me too 👀 . I am still verifying this is not a wash |
…he device
The kernel is bound by issue pressure on the LDS pipe: 0.00% bank conflicts,
MemUnitBusy 38.6%, and only 3.03 VALU instructions per LDS instruction against
7-9 for the token-by-token kernel. CU mode confines a block to one CU, which
halves the LDS contention domain, and it costs no residency here - a WGP holds
floor(128 KB / 57.25 KB) = 2 of these blocks in WGP mode and 2*floor(64 KB /
57.25 KB) = 2 in CU mode.
It only pays once every CU has a block to run. A block occupies one CU in CU
mode but spreads over the whole WGP in WGP mode, so below one block per WGP the
second CU of each WGP sits idle. Measured at 512 tokens, cost per op against
block count, with nsm = 20 WGPs on gfx1151:
blocks 4 8 16 20 24 32 40 48
delta +40% +40% +33% +19% -17% -9% -9% -9%
The launch therefore picks between two entry points on H*n_seqs > nsm. The
work-group processor mode is a function attribute rather than something a
template parameter can select, so the two entry points share one inlined body
and differ only in that attribute.
For head counts that take the CU path the op is 6.4-10.6% cheaper across every
sequence length from 64 to 4096 tokens, worth +0.67% end to end on
Qwen3.5-35B-A3B at pp4096 - what a tenth off an op that is ~7% of prefill
predicts. Head counts below the threshold keep the WGP path: Qwen3.5-0.8B, whose
16 heads take it, measures +0.00% at pp128 over ten paired iterations.
Assisted-by: Claude Opus 4.7
bd9250f to
d09a120
Compare
|
Does this produce larger end2end wins at higher contexts? Otherwise I think a +0.67% win is too small to go through the maintenance and upstreaming hussle. |
Kernel wise the win is around 6-9%. e2e tho, since we already improved GDN quite a lot is not that noticeable. If we happen to keep optimizing the rest of the model this +0.67% could become +2%. We can keep this PR as a draft until that happens if you prefer. |
What
The fused GDN kernel is bound by issue pressure on the LDS pipe, not by bandwidth or arithmetic:
SQ_LDS_BANK_CONFLICT / SQ_LDS_IDX_ACTIVEis 0.00%, MemUnitBusy is 38.6%, andSQ_INSTS_VALU / SQ_INSTS_LDSis 3.03 against 7-9 for the token-by-token kernel it replaced.CU mode confines a block to one CU, which halves the LDS contention domain. It costs no residency here: a WGP holds
floor(128 KB / 57.25 KB) = 2of these blocks in WGP mode and2*floor(64 KB / 57.25 KB) = 2in CU mode, so the resident block count is the same either way.It is applied per kernel with
__attribute__((target("cumode"))), which flips.amdhsa_workgroup_processor_modefor that kernel alone — no build-system change, and nothing else in the backend is affected.The gate
CU mode only pays once every CU has a block to run. A block occupies one CU in CU mode but spreads over the whole WGP in WGP mode, so below one block per WGP the second CU of each WGP sits idle. Cost per op at 512 tokens against block count (
H_v * n_seqs), on gfx1151 wherensm= 20 WGPs:The crossover falls exactly on
nsm, so the launch picks between two entry points onH * n_seqs > nsm. The work-group processor mode is a function attribute rather than something a template parameter can select, so the two entry points share one inlined body and differ only in that attribute.Results
Op time, interleaved with alternating order, medians of 8-10:
The head counts come from the models themselves, not from an assumption: the 35B stages
q/kas[128, 16, T, 1]butvas[128, 32, T, 1]with state[128, 128, 32, 1], so it is 32 v-heads behind a 2-way GQA, and the dispatch keys onnev1(v-heads), not on the q/k head count.Head counts on the CU path are 6.4-10.6% cheaper at every sequence length from 64 to 4096 tokens. Head counts below the threshold keep the WGP path and are unchanged; without the gate,
H_v = 16would have regressed 23-34%.End to end
GDN is now only ~7% of prefill, so a tenth off the op predicts about +0.7% throughput. That is close to the noise floor, so this was measured with the build order alternated every iteration and with the median of
samples_tsafter discarding the first repetition — llama-bench's first repetition is systematically cold (a 0.8B pp128 series reads4572, 8368, 8419, 8401, ...) andavg_tsaverages over it, which reads ~6% low and swamps an effect this size.Six iterations per cell,
-ub 512:The 35B pp4096 cell is the one that clears the noise: the two sample ranges barely touch (
1784-1792against1769-1783), and +0.67% is what the op-level -9% predicts.The 0.8B keeps the WGP path, and a further ten iterations paired per iteration confirm it is untouched: pp128 comes out at a +0.04% median with the new build ahead in 5 of 10 pairs. Its pp4096 cell reads -0.29% but two of the ten pairs are the first and last measurement of the series, both of which landed on the baseline and both elevated (10316 and 10074 against a ~9600 steady state); dropping them leaves -0.19% with pair deltas scattered from -0.79% to +1.3%. There is also no mechanism for a real loss there — the refactor moved the WGP kernel by +0.3 to +0.5% at op level, and GDN is a small enough share of that model's prefill that this is ~0.03% end to end.
Beyond throughput, the op does the same work in ~10% less GPU time and energy, which matters on an APU and leaves more of the device for other ops.
Testing
test-backend-ops -o GATED_DELTA_NET: 42/42 pass with the path enabled and disabled. Both entry points report VGPR 91, zero spill, zero scratch, 16 waves/SIMD and LDS 58624 B, and.amdhsa_workgroup_processor_mode1 and 0 respectively.