Skip to content

Complete the GPU extensions: atomic get/set, swap, float min/max - #80

Merged
vchuravy merged 1 commit into
mainfrom
vc/complete-gpu-extensions
Sep 8, 2026
Merged

vchuravy merged 1 commit into
mainfrom
vc/complete-gpu-extensions

Conversation

@vchuravy

@vchuravy vchuravy commented Sep 8, 2026

Copy link
Copy Markdown
Member

The CUDA, oneAPI, OpenCL and Metal extensions implemented Atomix.get and Atomix.set! as error("not implemented"), and modify! errored for any operation without a listed backend intrinsic. So inside kernels @atomic A[i], @atomic A[i] = v, @atomicswap A[i] = v (which is modify! with right) and float @atomic max(A[i], x) / min all threw — silently on backends that do not surface device exceptions (pocl leaves the array untouched), and with a MethodError on CUDA, whose atomic_max!/atomic_min! only exist for integers. This surfaced in JuliaGPU/KernelAbstractions.jl#726 and #737.

Changes

  • get / set!: the overrides are removed, so Atomix's generic implementation applies: an LLVM atomic load/store on the device pointer through UnsafeAtomicsLLVM, which honors the requested ordering. Metal uses its atomic_load_explicit / atomic_store_explicit intrinsics instead, since Metal.jl does not lower LLVM atomics.
  • Swap: modify! with right dispatches to the backend's exchange intrinsic. On CUDA, floats are exchanged through their integer representation (atomic_xchg! is integer-only there).
  • No native instruction (float min/max where the backend lacks them, arbitrary functions such as *): a compare-and-swap loop on the backend's CAS. It compares bitwise (===), so a stored NaN or a signed zero cannot make it spin. On OpenCL/oneAPI the float min/max intrinsics are used when the installed SPIRVIntrinsics provides them (checked with hasmethod at extension load), otherwise the loop.
  • Dispatch by element type: native intrinsics are only selected for the types the backend actually supports (Int32/Int64/UInt32/UInt64, Float32/Float64; Int32/UInt32/Float32 on Metal), everything else takes the CAS path instead of a MethodError. Float64 add on CUDA checks the compute capability, as CUDA.jl's own atomic_arrayset does.
  • Orderings are still ignored on the intrinsic paths (they have no ordering parameter); this is now stated in each extension instead of a TODO.

Tests

Each backend test file gains test_get_set (including the @atomic A[i] / @atomic :monotonic A[i] = v sugar), test_swap (Atomix.swap! and @atomicswap), test_ordering, and test_float (add, sub, max, min, and * via the CAS loop). The stale "Not implemented" comment blocks are gone.

Run locally on Julia 1.10:

  • --CUDA (CUDA v6.3.1, Quadro RTX 4000): 8/8 testsets pass.
  • --OpenCL (OpenCL v0.10.11, pocl 7.1): 8/8 testsets pass.
  • KernelAbstractions' new atomics testsuite on OpenCLBackend/pocl: 38/38 pass, where 5 failed before.
  • oneAPI and Metal are untested locally; relying on Buildkite.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DnYMbgSmZQr7KtRUwei63a

The CUDA, oneAPI, OpenCL and Metal extensions implemented `Atomix.get`
and `Atomix.set!` as `error("not implemented")`, and `modify!` errored
for any operation without a listed backend intrinsic. As a result
`@atomic A[i]`, `@atomic A[i] = v`, `@atomicswap`, and float `max`/`min`
threw inside kernels (silently, on backends that do not report device
exceptions).

- `get`/`set!` now use the generic implementation: an LLVM atomic
  load/store through UnsafeAtomicsLLVM, honoring the ordering. Metal uses
  its `atomic_load_explicit`/`atomic_store_explicit` intrinsics instead.
- Swap dispatches to the backend's exchange intrinsic; floats on CUDA go
  through their integer representation.
- Operations without a native instruction (float min/max where the
  backend lacks them, arbitrary functions) use a compare-and-swap loop
  that compares bitwise, so NaN and signed zeros cannot make it spin.
- Native dispatch is by element type, so unsupported types fall back
  instead of hitting a MethodError.

Adds get/set, swap, ordering and floating-point tests for each backend.

Assisted-by: Claude Code (Fable 5.1)
@vchuravy
vchuravy requested a review from maleadt September 8, 2026 08:52
@vchuravy
vchuravy merged commit ddd205b into main Sep 8, 2026
10 of 12 checks passed
@vchuravy
vchuravy deleted the vc/complete-gpu-extensions branch September 8, 2026 17:06
vchuravy added a commit to JuliaGPU/KernelAbstractions.jl that referenced this pull request Sep 8, 2026
Atomix 1.2 completes its GPU extensions (JuliaConcurrent/Atomix.jl#80):
atomic load/store, swap, and a compare-and-swap fallback for operations
without a native instruction, such as floating-point min/max on CUDA.
With that, float min/max is portable again, so run the min/max testsuite
cases for floats as well and update the "Supported operations" table.

Assisted-by: Claude Code (Fable 5.1)
@christiangnrd

Copy link
Copy Markdown
Contributor

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