Skip to content

Fit the reductions' partial results in local memory for large element types - #204

Merged
maleadt merged 1 commit into
mainfrom
tb/reduce-localmem
Nov 8, 2026
Merged

maleadt merged 1 commit into
mainfrom
tb/reduce-localmem

Conversation

@maleadt

@maleadt maleadt commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Every reduction kernel keeps a partial result per thread in local memory, in blocks of 256 threads by default, without checking the element size. Elements larger than 128 bytes exceed the 32 KiB budget that the sorts and scans keep to, and those larger than 192 bytes fail to compile on CUDA devices before Blackwell (48 KiB of static shared memory; about 396 bytes on Blackwell). ForwardDiff duals with 16 partials, or nested ones, are that large: JuliaGPU/CUDA.jl#3516.

As CUB's MemBoundScaling, an unset block size is now lowered to the largest power of two whose partial results fit the tuning's new local_mem_bytes. The size is that of the partial-result type, an Optional for operators without a neutral element, which resolve_reduce takes as an optional argument like resolve_scan. The second pass of split reductions stays within the first pass's block size. An explicit block_size is kept as given.

Tests: resolution of the block size for elements of 4 bytes to 64 KiB, and reductions of 648- and 2080-byte elements (blocks of 32 and 8) along every launch shape, which fail to compile without this change. On AMDGPU, ordered reductions of the larger size need AMDGPU_LLVM_Backend_jll 23.1.3+1 (JuliaPackaging/Yggdrasil#15464, backporting llvm/llvm-project#239163): LLVM's AMDGPU back-end crashed on the kernel's divergent endpgm once it outgrew the branch range.

Sub-group reductions, which shrink the kernels' local memory further, follow in a stacked PR. Ordered reductions of elements of more than 32 words also need JuliaGPU/KernelAbstractions.jl#947, so this should be merged after KernelInterface 0.4.5 is registered.

… types

Every reduction kernel keeps a partial result per thread in local memory, in blocks of 256
threads by default, so elements of more than 128 bytes exceeded the 32 KiB budget that the
sorts and scans keep to, and those of more than 192 bytes failed to compile on CUDA devices
before Blackwell (48 KiB of static shared memory). ForwardDiff duals with 16 partials, or nested
ones, are that large.

As CUB's `MemBoundScaling`, lower an unset block size to the largest power of two whose partial
results fit the tuning's new `local_mem_bytes`, sized by the partial-result type (an `Optional`
for operators without a neutral element), and keep the second pass of split reductions within
the first pass's block size. An explicit `block_size` is kept.

x-ref JuliaGPU/CUDA.jl#3516
@maleadt
maleadt force-pushed the tb/reduce-localmem branch from f40b208 to 7eb9b2b Compare November 6, 2026 09:59
@maleadt
maleadt merged commit 5cadd1b into main Nov 8, 2026
17 checks passed
@maleadt
maleadt deleted the tb/reduce-localmem branch November 8, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant