Repository navigation
Faster strided reductions into contiguous outputs (dims=2 of matrices, middle dimensions of arrays) - #176
Merged
Conversation
…ut underfills A strided reduction into contiguous outputs with at least as many outputs as elements in each took one thread per output (the tiled kernel only exactly square ones), whose `dst_size` threads leave a GPU mostly idle below tens of thousands of outputs: summing a 3072×2048 BFloat16 matrix along `dims=2` took 90 µs on an RTX 5080. These reductions now take the tiled kernel, 8 outputs per block in one pass, where tiles of 32 outputs would not fill the device, and the columns kernel where they would; one thread per output only for rows of at least 64 KiB of outputs, or reductions too short to share. That matrix takes 16.9 µs, and an 8192×8192 Float32 one 295 instead of 416 µs.
The columns kernel took only contiguous outputs, so reducing the middle dimension of a 3-d array (`sum(x; dims=2)` of a `(features, sequence, batch)` array) fell back to one thread or one block per output: 42 µs for a 768×1024×8 BFloat16 array on an RTX 5080, a third of the bandwidth, and 275 µs on an RTX 6000 Ada. It now reduces runs of contiguous outputs at the offsets of the other kept dimensions, its tiles covering every run: that array takes 19.6 µs on the RTX 5080 and 21.5 µs on the RTX 6000 Ada, and summing a 64×4096×32 Float32 array along its second dimension 41 instead of 138 µs on the RTX 5080.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to JuliaGPU/CUDA.jl#3446. Base reductions of GPU arrays go through
AK.mapreducedim!, and two common layouts of strided reductions into contiguous outputs ran far below bandwidth:sum(x; dims=2)of a 3072×2048 matrix, took one thread per output.dst_sizethreads leave the GPU mostly idle below tens of thousands of outputs: 90 µs for that BFloat16 matrix on an RTX 5080, 140 GB/s. Only exactly square shapes took the tiled kernel.sum(x; dims=2)of a(features, sequence, batch)array, fell back to one thread or one block per output, since the columns kernel only took contiguous outputs: 46–300 GB/s.This PR:
_columns_shape:Both are in the spirit of PyTorch's
Reduce.cuh: a fixed 2-d block shape for strided reductions, split across blocks only when the outputs alone don't fill the device. No new tuning parameters.Timings
sum(x; dims=2), base → this PR (RTX 5080 and RTX 6000 Ada: GPU time; Arc A770 and M1:AK.reduceper call, back to back):Geometric mean of per-call time over 26 shapes (Float32 and 16-bit, base = 1): RTX 5080 0.58, RTX 6000 Ada 0.60, Arc A770 0.47, M1 0.80, Ryzen 9950X iGPU 0.71, PoCL 0.67. The other shapes in CUDA.jl#3446 are unchanged.
Known trade-off: on devices with a few compute units (a Ryzen 9950X's iGPU, Jetson Xavier), 2048×768 takes 1.4× as long. One thread per output already fills them with 8 blocks, and making that threshold device-relative didn't seem worth the extra tuning.
Tried and left out
global_reduce: zeroing the counters costs as much as the pass it saves.Tested with the reduce tests on CUDA, oneAPI, Metal, AMDGPU and OpenCL (PoCL), plus the full test suite on CUDA.