alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
AtomicGenericSycl.hpp
Go to the documentation of this file.
1/* Copyright 2023 Jan Stephan, Andrea Bocci, Luca Ferragina
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
11
12#include <cstdint>
13#include <type_traits>
14
15#ifdef ALPAKA_ACC_SYCL_ENABLED
16
17# include <sycl/sycl.hpp>
18
19namespace alpaka
20{
21 //! The SYCL accelerator atomic ops.
22 //
23 // Atomics can used in the hierarchy level grids, blocks and threads.
24 // Atomics are not guaranteed to be safe between devices
25 class AtomicGenericSycl
26 {
27 };
28
29 namespace detail
30 {
31 template<typename THierarchy>
32 struct SyclMemoryScope
33 {
34 };
35
36 template<>
37 struct SyclMemoryScope<hierarchy::Grids>
38 {
39 static constexpr auto value = sycl::memory_scope::device;
40 };
41
42 template<>
43 struct SyclMemoryScope<hierarchy::Blocks>
44 {
45 static constexpr auto value = sycl::memory_scope::device;
46 };
47
48 template<>
49 struct SyclMemoryScope<hierarchy::Threads>
50 {
51 static constexpr auto value = sycl::memory_scope::work_group;
52 };
53
54 template<typename T, typename THierarchy>
55 using sycl_atomic_ref = sycl::atomic_ref<T, sycl::memory_order::relaxed, SyclMemoryScope<THierarchy>::value>;
56
57 template<typename THierarchy, typename T, typename TOp>
58 inline auto callAtomicOp(T* const addr, TOp&& op)
59 {
60 auto ref = sycl_atomic_ref<T, THierarchy>{*addr};
61 return op(ref);
62 }
63
64 template<typename TRef, typename T, typename TEval>
65 inline auto casWithCondition(T* const addr, TEval&& eval)
66 {
67 auto ref = TRef{*addr};
68 auto old_val = ref.load();
69
70 // prefer compare_exchange_weak when in a loop, assuming that eval is not expensive
71 while(!ref.compare_exchange_weak(old_val, eval(old_val)))
72 {
73 }
74
75 return old_val;
76 }
77 } // namespace detail
78} // namespace alpaka
79
80namespace alpaka::trait
81{
82 // Add.
83 //! The SYCL accelerator atomic operation.
84 template<typename T, typename THierarchy>
85 struct AtomicOp<AtomicAdd, AtomicGenericSycl, T, THierarchy>
86 {
87 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "SYCL atomics do not support this type");
88
89 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
90 {
91 return alpaka::detail::callAtomicOp<THierarchy>(
92 addr,
93 [&value](auto& ref) { return ref.fetch_add(value); });
94 }
95 };
96
97 // Sub.
98 //! The SYCL accelerator atomic operation.
99 template<typename T, typename THierarchy>
100 struct AtomicOp<AtomicSub, AtomicGenericSycl, T, THierarchy>
101 {
102 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "SYCL atomics do not support this type");
103
104 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
105 {
106 return alpaka::detail::callAtomicOp<THierarchy>(
107 addr,
108 [&value](auto& ref) { return ref.fetch_sub(value); });
109 }
110 };
111
112 // Min.
113 //! The SYCL accelerator atomic operation.
114 template<typename T, typename THierarchy>
115 struct AtomicOp<AtomicMin, AtomicGenericSycl, T, THierarchy>
116 {
117 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "SYCL atomics do not support this type");
118
119 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
120 {
121 return alpaka::detail::callAtomicOp<THierarchy>(
122 addr,
123 [&value](auto& ref) { return ref.fetch_min(value); });
124 }
125 };
126
127 // Max.
128 //! The SYCL accelerator atomic operation.
129 template<typename T, typename THierarchy>
130 struct AtomicOp<AtomicMax, AtomicGenericSycl, T, THierarchy>
131 {
132 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "SYCL atomics do not support this type");
133
134 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
135 {
136 return alpaka::detail::callAtomicOp<THierarchy>(
137 addr,
138 [&value](auto& ref) { return ref.fetch_max(value); });
139 }
140 };
141
142 // Exch.
143 //! The SYCL accelerator atomic operation.
144 template<typename T, typename THierarchy>
145 struct AtomicOp<AtomicExch, AtomicGenericSycl, T, THierarchy>
146 {
147 static_assert(
148 (std::is_integral_v<T> || std::is_floating_point_v<T>) and(sizeof(T) == 4 || sizeof(T) == 8),
149 "SYCL atomics do not support this type");
150
151 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
152 {
153 return alpaka::detail::callAtomicOp<THierarchy>(addr, [&value](auto& ref) { return ref.exchange(value); });
154 }
155 };
156
157 // Inc.
158 //! The SYCL accelerator atomic operation.
159 template<typename T, typename THierarchy>
160 struct AtomicOp<AtomicInc, AtomicGenericSycl, T, THierarchy>
161 {
162 static_assert(
163 std::is_unsigned_v<T> && (sizeof(T) == 4 || sizeof(T) == 8),
164 "SYCL atomics support only 32- and 64-bits unsigned integral types");
165
166 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
167 {
168 auto inc = [&value](auto old_val)
169 { return (old_val >= value) ? static_cast<T>(0) : (old_val + static_cast<T>(1)); };
170 return alpaka::detail::casWithCondition<alpaka::detail::sycl_atomic_ref<T, THierarchy>>(addr, inc);
171 }
172 };
173
174 // Dec.
175 //! The SYCL accelerator atomic operation.
176 template<typename T, typename THierarchy>
177 struct AtomicOp<AtomicDec, AtomicGenericSycl, T, THierarchy>
178 {
179 static_assert(
180 std::is_unsigned_v<T> && (sizeof(T) == 4 || sizeof(T) == 8),
181 "SYCL atomics support only 32- and 64-bits unsigned integral types");
182
183 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
184 {
185 auto dec = [&value](auto& old_val)
186 { return ((old_val == 0) || (old_val > value)) ? value : (old_val - static_cast<T>(1)); };
187 return alpaka::detail::casWithCondition<alpaka::detail::sycl_atomic_ref<T, THierarchy>>(addr, dec);
188 }
189 };
190
191 // And.
192 //! The SYCL accelerator atomic operation.
193 template<typename T, typename THierarchy>
194 struct AtomicOp<AtomicAnd, AtomicGenericSycl, T, THierarchy>
195 {
196 static_assert(std::is_integral_v<T>, "Bitwise operations only supported for integral types.");
197
198 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
199 {
200 return alpaka::detail::callAtomicOp<THierarchy>(
201 addr,
202 [&value](auto& ref) { return ref.fetch_and(value); });
203 }
204 };
205
206 // Or.
207 //! The SYCL accelerator atomic operation.
208 template<typename T, typename THierarchy>
209 struct AtomicOp<AtomicOr, AtomicGenericSycl, T, THierarchy>
210 {
211 static_assert(std::is_integral_v<T>, "Bitwise operations only supported for integral types.");
212
213 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
214 {
215 return alpaka::detail::callAtomicOp<THierarchy>(addr, [&value](auto& ref) { return ref.fetch_or(value); });
216 }
217 };
218
219 // Xor.
220 //! The SYCL accelerator atomic operation.
221 template<typename T, typename THierarchy>
222 struct AtomicOp<AtomicXor, AtomicGenericSycl, T, THierarchy>
223 {
224 static_assert(std::is_integral_v<T>, "Bitwise operations only supported for integral types.");
225
226 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& value) -> T
227 {
228 return alpaka::detail::callAtomicOp<THierarchy>(
229 addr,
230 [&value](auto& ref) { return ref.fetch_xor(value); });
231 }
232 };
233
234 // Cas.
235 //! The SYCL accelerator atomic operation.
236 template<typename T, typename THierarchy>
237 struct AtomicOp<AtomicCas, AtomicGenericSycl, T, THierarchy>
238 {
239 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "SYCL atomics do not support this type");
240
241 static auto atomicOp(AtomicGenericSycl const&, T* const addr, T const& expected, T const& desired) -> T
242 {
243 auto cas = [&expected, &desired](auto& ref)
244 {
245 auto expected_ = expected;
246 // Atomically compares the value of `ref` with the value of `expected`.
247 // If the values are equal, replaces the value of `ref` with `desired`.
248 // Otherwise updates `expected` with the value of `ref`.
249 // Returns a bool telling us if the exchange happened or not, but the Alpaka API does not make use of
250 // it.
251 ref.compare_exchange_strong(expected_, desired);
252
253 // If the update succeded, return the previous value of `ref`.
254 // Otherwise, return the current value of `ref`.
255 return expected_;
256 };
257
258 return alpaka::detail::callAtomicOp<THierarchy>(addr, cas);
259 }
260 };
261} // namespace alpaka::trait
262
263#endif
The accelerator traits.
The alpaka accelerator library.
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_HOST_ACC auto atomicOp(TAtomic const &atomic, T *const addr, T const &value, THierarchy const &=THierarchy()) -> T
Executes the given operation atomically.
Definition Traits.hpp:73