Add Atomix and UnsafeAtomics atomics tests - #726
Conversation
| if !(backend() isa CPU) | ||
| @test_skip "UnsafeAtomics tests only run on the CPU backend" | ||
| return | ||
| end |
There was a problem hiding this comment.
| if !(backend() isa CPU) | |
| @test_skip "UnsafeAtomics tests only run on the CPU backend" | |
| return | |
| end |
There was a problem hiding this comment.
Done in 291db15 — the CPU-only gate is removed, so the UnsafeAtomics tests now run on every backend.
vchuravy
left a comment
There was a problem hiding this comment.
Missing:
- fences
- orderings
- scopes
Benchmark ResultsShow table
Benchmark PlotsA plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. |
| @testset "Atomix" begin | ||
| # Float32 is excluded since atomic float add requires the SPIR-V | ||
| # extension SPV_EXT_shader_atomic_float_add, unavailable with PoCL. | ||
| @testset "atomic add ($T)" for T in (Int32, UInt32) |
There was a problem hiding this comment.
We should implement fallbacks for this JuliaGPU/GPUCompiler.jl#652
There was a problem hiding this comment.
Added a TODO comment referencing JuliaGPU/GPUCompiler.jl#652 next to the Float32 exclusion (291db15).
I just tested it, PoCL supports julia> using OpenCL, KernelAbstractions, SPIRVIntrinsics, pocl_jll
julia> gentype = Float32; as = 1
1
julia> @eval SPIRVIntrinsics @device_function atomic_max!(p::LLVMPtr{$gentype,$as}, val::$gentype) =
@builtin_ccall("__spirv_AtomicFMaxEXT", $gentype,
(LLVMPtr{$gentype,$as}, UInt32, UInt32, $gentype),
p, UInt32(atomic_scope),
UInt32(atomic_memory_semantics(Val($as))), val)
julia> function atomix_max!(A)
@inbounds KernelAbstractions.@atomic max(A[1], eltype(A)(get_global_id()))
return nothing
end
atomix_max! (generic function with 4 methods)
julia> a = OpenCL.zeros(T)
0-dimensional CLArray{Float32, 0, OpenCL.cl.UnifiedDeviceMemory}:
0.0
julia> @opencl global_size = 1000 extensions = ["SPV_EXT_shader_atomic_float_min_max"] atomix_max!(a)
OpenCL.HostKernel{typeof(atomix_max!), Tuple{CLDeviceArray{Float32, 0, 1}}}(atomix_max!, OpenCL.Kernel("_Z11atomix_max_13CLDeviceArrayI7Float32Li0ELi1EE" nargs=2), false)
julia> a
0-dimensional CLArray{Float32, 0, OpenCL.cl.UnifiedDeviceMemory}:
1000.0Probably makes sense to enable by default for PoCLBackend, or what do you think? |
|
Addressed the review in 291db15:
25 tests passing on the CPU backend. |
291db15 to
f7698e2
Compare
a6ba637 to
3a92bd8
Compare
|
Does this close #308? |
0452642 to
0e8a63e
Compare
Adds an Atomics testsuite exercising @atomic/@atomicswap/@atomicreplace (via Atomix) in kernels on all backends, plus UnsafeAtomics pointer-based atomics on the CPU backend. UnsafeAtomics becomes a direct test dependency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gs, fences, and syncscopes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nsions Run the Atomix and UnsafeAtomics add and min/max tests for Float32 and, where the backend supports it, Float64 in addition to the integer types. Assisted-by: Claude Code (Fable 5.1)
UnsafeAtomics emits `atomicrmw fmin`/`fmax` directly and has no fallback where the target lacks the instruction; OpenCL.jl's OpenCL C program backend cannot enable the SPIR-V float min/max extension, so those cases fail there. Atomix's float min/max (with its compare-and-swap fallback) remains tested on every backend. Assisted-by: Claude Code (Fable 5.1)
`UnsafeAtomics.add!` on floats emits `atomicrmw fadd`, which needs the SPV_EXT_shader_atomic_float_add extension that NVIDIA's OpenCL driver does not provide, so the float cases fail on the OpenCL (CUDA) backend like the float min/max ones did on the OpenCL C backend. Raw UnsafeAtomics has no fallback; Atomix covers the float operations portably. Assisted-by: Claude Code (Fable 5.1)
NVPTX refuses to select it: `LLVM ERROR: Unsupported scope "Thread" for seq_cst fence` (Julia 1.12, LLVM 18). The single-thread scoped store and add stay. Assisted-by: Claude Code (Fable 5.1)
NVPTX rejects single-thread scoped atomics outright on LLVM 21 (`LLVM ERROR: Atomics need scope > "Thread"`), so the kernel now exercises the explicit `none` scope for store, fence and add. Assisted-by: Claude Code (Fable 5.1)
On Julia 1.10 and 1.13 UnsafeAtomics emits that fence through inline assembly, which the SPIR-V backend rejects (SPV_INTEL_inline_assembly). Release/acquire fences remain covered by the "fences" test. Assisted-by: Claude Code (Fable 5.1)
0e8a63e to
a644d16
Compare
- Load UnsafeAtomics through its UUID, like Pkg in testsuite.jl, so backend packages need not add it to their test environments. - Fuse the Atomix max and min kernels into one, mirroring the UnsafeAtomics form; halves the kernel variants compiled per type. - One UnsafeAtomics add kernel exercises the default and every explicit ordering on separate histogram columns, replacing a second kernel and a loop that compiled five variants for the same coverage. - Drop a dead assignment, a redundant type parameter, and stale comments about restrictions that no longer exist. Assisted-by: Claude Code (Fable 5.1)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #726 +/- ##
=======================================
Coverage 63.72% 63.72%
=======================================
Files 23 23
Lines 1935 1935
=======================================
Hits 1233 1233
Misses 702 702 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adds an
Atomicstestset to the shared testsuite covering the atomics functionality KernelAbstractions re-exports from Atomix, plus raw-pointer atomics from UnsafeAtomics.Atomix (all backends, gated on
supports_atomics(backend()))@atomic +=forInt32/UInt32/Float32/Float64@atomic max/@atomic minreductions across the ndrange@atomic A[i]) and store (@atomic A[i] = v)@atomicswap@atomicreplacewith both succeeding and failing CASUnsafeAtomics (CPU backend only,
@test_skipelsewhere)add!histogram forInt32/UInt32/Float32/Float64max!/min!store!/modify!/cas!/xchg!/loadsequenceadd!with explicitseq_cstorderingNotes:
SPV_EXT_shader_atomic_float_{add,min_max}extensions based oncl_ext_float_atomics, and extends thetest/atomics.jlit introduced: add and min/max run forFloat32and, wheresupports_float64,Float64through Atomix.skip_tests = Set(["Atomics"]).CI status
get/set!aserror("not implemented")and had no swap. Rebased ontomain, which includes [pocl] Enable float add and min/max atomics via cl_ext_float_atomics #737 and theAtomix = "1.2.1"compat.atomicrmw fadd/fmin/fmaxneed SPIR-V extensions that NVIDIA's OpenCL driver and OpenCL.jl's C backend do not provide, and UnsafeAtomics has no fallback. Atomix covers floats. The syncscope test uses the system scope only, since NVPTX rejects single-thread scoped atomics and fences.🤖 Generated with Claude Code