Skip to content

Commit 52241c2

Browse files
authored
Add OpenCL extension and Buildkite CI (#51)
1 parent 37c8b0d commit 52241c2

File tree

5 files changed

+166
-0
lines changed

5 files changed

+166
-0
lines changed

.buildkite/pipeline.yml

+19
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,22 @@ steps:
5656
intel: "*"
5757
if: build.message !~ /\[skip tests\]/
5858
timeout_in_minutes: 15
59+
60+
- label: "OpenCL.jl"
61+
plugins:
62+
- JuliaCI/julia#v1:
63+
version: "1.10"
64+
command: |
65+
julia -e 'using Pkg
66+
67+
println("--- :julia: Instantiating environment")
68+
Pkg.add(["OpenCL", "pocl_jll"])
69+
Pkg.develop(PackageSpec(name="Atomix", path="."))
70+
71+
println("+++ :julia: Running tests")
72+
Pkg.test("Atomix", test_args=["--OpenCL"])'
73+
agents:
74+
queue: "juliagpu"
75+
intel: "*"
76+
if: build.message !~ /\[skip tests\]/
77+
timeout_in_minutes: 15

Project.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ UnsafeAtomics = "013be700-e6cd-48c3-b4a1-df204f14c38f"
1010
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
1111
Metal = "dde4c033-4e86-420c-a63e-0dd931031962"
1212
oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b"
13+
OpenCL = "08131aa3-fb12-5dee-8b74-c09406e224a2"
1314

1415
[extensions]
1516
AtomixCUDAExt = "CUDA"
1617
AtomixMetalExt = "Metal"
1718
AtomixoneAPIExt = "oneAPI"
19+
AtomixOpenCLExt = "OpenCL"
1820

1921
[compat]
2022
CUDA = "5"
2123
Metal = "1"
2224
oneAPI = "1"
25+
OpenCL = "^0.10"
2326
UnsafeAtomics = "0.1, 0.2, 0.3"
2427
julia = "1.10"

ext/AtomixOpenCLExt.jl

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# TODO: respect ordering
2+
module AtomixOpenCLExt
3+
4+
using Atomix: Atomix, IndexableRef
5+
using OpenCL: SPIRVIntrinsics, CLDeviceArray
6+
7+
const CLIndexableRef{Indexable<:CLDeviceArray} = IndexableRef{Indexable}
8+
9+
function Atomix.get(ref::CLIndexableRef, order)
10+
error("not implemented")
11+
end
12+
13+
function Atomix.set!(ref::CLIndexableRef, v, order)
14+
error("not implemented")
15+
end
16+
17+
@inline function Atomix.replace!(
18+
ref::CLIndexableRef,
19+
expected,
20+
desired,
21+
success_ordering,
22+
failure_ordering,
23+
)
24+
ptr = Atomix.pointer(ref)
25+
expected = convert(eltype(ref), expected)
26+
desired = convert(eltype(ref), desired)
27+
begin
28+
old = SPIRVIntrinsics.atomic_cmpxchg!(ptr, expected, desired)
29+
end
30+
return (; old = old, success = old === expected)
31+
end
32+
33+
@inline function Atomix.modify!(ref::CLIndexableRef, op::OP, x, order) where {OP}
34+
x = convert(eltype(ref), x)
35+
ptr = Atomix.pointer(ref)
36+
begin
37+
old = if op === (+)
38+
SPIRVIntrinsics.atomic_add!(ptr, x)
39+
elseif op === (-)
40+
SPIRVIntrinsics.atomic_sub!(ptr, x)
41+
elseif op === (&)
42+
SPIRVIntrinsics.atomic_and!(ptr, x)
43+
elseif op === (|)
44+
SPIRVIntrinsics.atomic_or!(ptr, x)
45+
elseif op === xor
46+
SPIRVIntrinsics.atomic_xor!(ptr, x)
47+
elseif op === min
48+
SPIRVIntrinsics.atomic_min!(ptr, x)
49+
elseif op === max
50+
SPIRVIntrinsics.atomic_max!(ptr, x)
51+
else
52+
error("not implemented")
53+
end
54+
end
55+
return old => op(old, x)
56+
end
57+
58+
end # module AtomixOpenCLExt
59+

test/runtests.jl

+4
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,8 @@ elseif "--oneAPI" in ARGS
149149
import Pkg
150150
Pkg.add("oneAPI")
151151
include("test_atomix_oneapi.jl")
152+
elseif "--OpenCL" in ARGS
153+
import Pkg
154+
Pkg.add(["OpenCL", "pocl_jll"])
155+
include("test_atomix_opencl.jl")
152156
end

test/test_atomix_opencl.jl

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using OpenCL, pocl_jll
2+
using OpenCL: @allowscalar
3+
cl.platform!("pocl")
4+
5+
@testset "AtomixOpenCLExt:extension_found" begin
6+
@test !isnothing(Base.get_extension(Atomix, :AtomixOpenCLExt))
7+
end
8+
9+
10+
function opencl(f)
11+
function g()
12+
f()
13+
nothing
14+
end
15+
OpenCL.@opencl g()
16+
end
17+
18+
19+
# Not implemented:
20+
#=
21+
function test_get_set()
22+
A = CUDA.ones(Int, 3)
23+
cuda() do
24+
GC.@preserve A begin
25+
ref = Atomix.IndexableRef(A, (1,))
26+
x = Atomix.get(ref)
27+
Atomix.set!(ref, -x)
28+
end
29+
end
30+
@test collect(A) == [-1, 1, 1]
31+
end
32+
=#
33+
34+
35+
@testset "AtomixOpenCLExt:test_cas" begin
36+
idx = (
37+
data = 1,
38+
cas1_ok = 2,
39+
cas2_ok = 3,
40+
# ...
41+
)
42+
@assert minimum(idx) >= 1
43+
@assert maximum(idx) == length(idx)
44+
45+
A = OpenCL.zeros(Int32, length(idx))
46+
opencl() do
47+
GC.@preserve A begin
48+
ref = Atomix.IndexableRef(A, (1,))
49+
(old, success) = Atomix.replace!(ref, 0, 42)
50+
A[idx.cas1_ok] = old == 0 && success
51+
(old, success) = Atomix.replace!(ref, 0, 43)
52+
A[idx.cas2_ok] = old == 42 && !success
53+
end
54+
end
55+
@test collect(A) == [42, 1, 1]
56+
end
57+
58+
59+
@testset "AtomixOpenCLExt:test_inc" begin
60+
A = OpenCL.CLArray(Int32(1):Int32(3))
61+
opencl() do
62+
GC.@preserve A begin
63+
ref = Atomix.IndexableRef(A, (1,))
64+
pre, post = Atomix.modify!(ref, +, 1)
65+
A[2] = pre
66+
A[3] = post
67+
end
68+
end
69+
@test collect(A) == [2, 1, 2]
70+
end
71+
72+
73+
@testset "AtomixOpenCLExt:test_inc_sugar" begin
74+
A = OpenCL.ones(Int32, 3)
75+
opencl() do
76+
GC.@preserve A begin
77+
@atomic A[begin] += 1
78+
end
79+
end
80+
@test collect(A) == [2, 1, 1]
81+
end

0 commit comments

Comments
 (0)