Skip to content

Use SIMD shuffles for native scans#874

Open
KookiesNKareem wants to merge 1 commit into
JuliaGPU:mainfrom
KookiesNKareem:agent/use-simd-shuffles-for-native-scans
Open

Use SIMD shuffles for native scans#874
KookiesNKareem wants to merge 1 commit into
JuliaGPU:mainfrom
KookiesNKareem:agent/use-simd-shuffles-for-native-scans

Conversation

@KookiesNKareem

@KookiesNKareem KookiesNKareem commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Use SIMD shuffles to reduce synchronization overhead in the native scan implementation.

The existing partial scan uses a Blelloch tree in threadgroup memory. For a 1024-thread block, this requires roughly 21 threadgroup barriers. The new path:

  • scans values within each 32-lane SIMD group using simd_shuffle_up
  • stores one total per SIMD group
  • scans those group totals with the first SIMD group
  • adds the preceding-group total to each local prefix

This requires no barriers for scans of up to 32 elements and two barriers for the hierarchical scan.

The SIMD path is limited to types supported by the existing shuffle intrinsics. Based on the measured crossover, 64–256-thread scans retain the existing Blelloch implementation, while 512-thread and larger scans use the hierarchical SIMD implementation. Unsupported types also continue to use the existing implementation.

The multi-block scan logic and algorithm selection between native kernels and MPSGraph are unchanged. Floating-point accumulate(max/min, ...) uses the native path automatically because MPSGraph does not preserve Julia's NaN propagation semantics.

Performance

Benchmarked on an Apple M4 with Julia 1.12.5. Each case scans 4,194,304 total elements, with 40 alternating before/after samples. Reported values are medians.

Native addition:

Type Elements per scan Before After Speedup
Float32 32 3.54 ms 2.33 ms 1.52x
Float32 512 5.06 ms 2.44 ms 2.07x
Float32 1024 5.44 ms 2.43 ms 2.24x
Int32 32 3.91 ms 2.28 ms 1.72x
Int32 512 5.26 ms 2.72 ms 1.94x
Int32 1024 5.50 ms 2.45 ms 2.25x

Floating-point max/min scans, which automatically use the native implementation:

Operation Elements per scan Before After Speedup
accumulate(max) 32 1.45 ms 0.90 ms 1.62x
accumulate(max) 512 1.33 ms 0.92 ms 1.45x
accumulate(max) 1024 2.21 ms 1.19 ms 1.85x
accumulate(min) 32 1.27 ms 0.81 ms 1.58x
accumulate(min) 512 1.50 ms 1.02 ms 1.47x
accumulate(min) 1024 1.73 ms 0.92 ms 1.89x

The 256-thread case continues to dispatch to the existing implementation because the SIMD implementation did not consistently win at that crossover.

Correctness

The operand order and handling of padded lanes and initial values are preserved. Floating-point association can differ, as it already can with the existing parallel Blelloch scan.

The added tests cover a non-power-of-two multi-block addition scan and a non-additive scan along a non-leading dimension. Existing tests cover small scans, initializers, supported operations, and NaN propagation.

Validation:

  • array test suite: 605/605 passed
  • SIMD intrinsic test suite: 196/196 passed

AI Use:
This PR was made with the assistance of GPT 5.6 Sol Extra High. I reviewed all the code and benchmark results myself.

@KookiesNKareem

Copy link
Copy Markdown
Contributor Author

@maleadt The Julia 1.13 job seems to have timed out after an hour rather than hitting a test failure. The array test group containing these changes passed. Not sure if this is still somehow a PR issue or just an innocent timeout. Nightly also soft-failed with the same GPUCompiler precompile error as main.

@christiangnrd

Copy link
Copy Markdown
Member

Thanks for the PR! Did you use AI for this PR? If so I kindly ask that you update the description with details as requested in the readme

@KookiesNKareem

Copy link
Copy Markdown
Contributor Author

Yeah, of course, sorry about that. I updated the description.

@KookiesNKareem
KookiesNKareem marked this pull request as ready for review July 15, 2026 19:03
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.

2 participants