alpaka
Abstraction Library for Parallel Kernel Acceleration
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 
7 #include "alpaka/atomic/Op.hpp"
11 
12 #include <cstdint>
13 #include <type_traits>
14 
15 #ifdef ALPAKA_ACC_SYCL_ENABLED
16 
17 # include <sycl/sycl.hpp>
18 
19 namespace 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 
80 namespace 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