alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
WarpGenericSycl.hpp
Go to the documentation of this file.
1/* Copyright 2026 Jan Stephan, Luca Ferragina, Andrea Bocci, Aurora Perego, Simone Balducci
2 * SPDX-License-Identifier: MPL-2.0
3 *
4 * The implementations of Shfl::shfl(), ShflUp::shfl_up(), ShflDown::shfl_down() and ShflXor::shfl_xor() are derived
5 * from Intel DPCT.
6 * Copyright (C) Intel Corporation.
7 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 * See https://llvm.org/LICENSE.txt for license information.
9 */
10
11#pragma once
12
16
17#include <cstdint>
18
19#ifdef ALPAKA_ACC_SYCL_ENABLED
20
21# include <sycl/sycl.hpp>
22
23namespace alpaka::warp
24{
25 //! The SYCL warp.
26 template<typename TDim>
27 class WarpGenericSycl : public interface::Implements<alpaka::warp::ConceptWarp, WarpGenericSycl<TDim>>
28 {
29 public:
30 using mask_type = std::uint32_t;
31
32 WarpGenericSycl(sycl::nd_item<TDim::value> my_item) : m_item_warp{my_item}
33 {
34 }
35
36 sycl::nd_item<TDim::value> m_item_warp;
37 };
38} // namespace alpaka::warp
39
40namespace alpaka::warp::detail
41{
42 // On Intel GPUs, the SYCL group operations used to implement the warp operations (group_ballot(),
43 // get_opportunistic_group() and the group algorithms) give wrong results when some work-items have returned
44 // early from the kernel, and the code calling them is not inlined, for example when compiling with -O0,
45 // -fno-inline or -fno-inline-functions. In these cases the work-items that have returned are still counted as
46 // members of the group. Clang-based compilers, like icpx, define __NO_INLINE__ in these cases.
47 // Define ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK to disable this check, for example if the kernels using the warp
48 // operations never return early.
49# if defined(ALPAKA_SYCL_ONEAPI_GPU) && defined(__NO_INLINE__) && !defined(ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK)
50 constexpr bool syclWarpRequiresInlining = true;
51# else
52 constexpr bool syclWarpRequiresInlining = false;
53# endif
54
55 // Dependent on the template parameter, so that the static_assert is only evaluated when a warp operation is used.
56 template<typename TDim>
57 inline constexpr bool syclWarpSupported = not syclWarpRequiresInlining;
58} // namespace alpaka::warp::detail
59
60# define ALPAKA_SYCL_WARP_CHECK_INLINE \
61 static_assert( \
62 alpaka::warp::detail::syclWarpSupported<TDim>, \
63 "The alpaka warp operations give wrong results on Intel GPUs when the SYCL code is compiled without " \
64 "inlining (e.g. -O0, -fno-inline or -fno-inline-functions). Enable optimisations (-O1 or higher) and " \
65 "inlining, or define ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK if the kernels never return early.")
66
67namespace alpaka::warp::trait
68{
69 // oneAPI up to 2025.3 uses sycl::ext::oneapi::experimental::this_kernel::get_opportunistic_group(),
70 // while oneAPI 2026.0 uses sycl::ext::oneapi::experimental::this_work_item::get_opportunistic_group()
71# if ALPAKA_COMP_ICPX >= ALPAKA_VERSION_NUMBER(2026, 0, 0)
72 using sycl::ext::oneapi::experimental::this_work_item::get_opportunistic_group;
73# else
74 using sycl::ext::oneapi::experimental::this_kernel::get_opportunistic_group;
75# endif
76
77 template<typename TDim>
78 struct GetSize<warp::WarpGenericSycl<TDim>>
79 {
80 static auto getSize(warp::WarpGenericSycl<TDim> const& warp) -> std::int32_t
81 {
82 auto const sub_group = warp.m_item_warp.get_sub_group();
83 // SYCL sub-groups are always 1D
84 return static_cast<std::int32_t>(sub_group.get_max_local_range()[0]);
85 }
86 };
87
88 template<typename TDim>
89 struct GetSizeCompileTime<warp::WarpGenericSycl<TDim>>
90 {
91 static constexpr auto getSizeCompileTime() -> std::int32_t
92 {
93 // SYCL sub-groups size is usually not known at compile time
94 return 0;
95 }
96 };
97
98 template<typename TDim>
99 struct GetSizeUpperLimit<warp::WarpGenericSycl<TDim>>
100 {
101 static constexpr auto getSizeUpperLimit() -> std::int32_t
102 {
103 // See include/alpaka/kernel/SyclSubgroupSize.hpp for possible sub-group sizes.
104 return 64;
105 }
106 };
107
108 template<typename TDim>
109 struct Activemask<warp::WarpGenericSycl<TDim>>
110 {
111 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
112 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
113 // Restrict to warpSize <= 32 for now.
114 static auto activemask(warp::WarpGenericSycl<TDim> const& /*warp*/) -> warp::WarpGenericSycl<TDim>::mask_type
115 {
116 ALPAKA_SYCL_WARP_CHECK_INLINE;
117
118 sycl::sub_group sg = sycl::ext::oneapi::this_work_item::get_sub_group();
119 auto const mask = sycl::ext::oneapi::group_ballot(sg, true);
120 std::uint32_t bits = 0;
121 mask.extract_bits(bits);
122 return bits;
123 }
124 };
125
126 template<typename TDim>
127 struct All<warp::WarpGenericSycl<TDim>>
128 {
129 static auto all(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate) -> std::int32_t
130 {
131 ALPAKA_SYCL_WARP_CHECK_INLINE;
132
133 auto activegroup = get_opportunistic_group();
134 return static_cast<std::int32_t>(sycl::all_of_group(activegroup, static_cast<bool>(predicate)));
135 }
136 };
137
138 template<typename TDim>
139 struct Any<warp::WarpGenericSycl<TDim>>
140 {
141 static auto any(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate) -> std::int32_t
142 {
143 ALPAKA_SYCL_WARP_CHECK_INLINE;
144
145 auto activegroup = get_opportunistic_group();
146 return static_cast<std::int32_t>(sycl::any_of_group(activegroup, static_cast<bool>(predicate)));
147 }
148 };
149
150 template<typename TDim>
151 struct Ballot<warp::WarpGenericSycl<TDim>>
152 {
153 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
154 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
155 // Restrict to warpSize <= 32 for now.
156 static auto ballot(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate)
157 -> warp::WarpGenericSycl<TDim>::mask_type
158 {
159 ALPAKA_SYCL_WARP_CHECK_INLINE;
160
161 auto sub_group = sycl::ext::oneapi::this_work_item::get_sub_group();
162 auto const mask = sycl::ext::oneapi::group_ballot(sub_group, static_cast<bool>(predicate));
163 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
164 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
165 // Restrict to warpSize <= 32 for now.
166 std::uint32_t bits = 0;
167 mask.extract_bits(bits);
168 return bits;
169 }
170 };
171
172 template<typename TDim>
173 struct Shfl<warp::WarpGenericSycl<TDim>>
174 {
175 template<typename T>
176 static auto shfl(
177 warp::WarpGenericSycl<TDim> const& /*warp*/,
178 T value,
179 std::int32_t srcLane,
180 std::int32_t width)
181 {
182 ALPAKA_SYCL_WARP_CHECK_INLINE;
183
184 ALPAKA_ASSERT_ACC(width > 0);
185 ALPAKA_ASSERT_ACC(srcLane >= 0);
186
187 /* If width < srcLane the sub-group needs to be split into assumed subdivisions. The first item of each
188 subdivision has the assumed index 0. The srcLane index is relative to the subdivisions.
189
190 Example: If we assume a sub-group size of 32 and a width of 16 we will receive two subdivisions:
191 The first starts at sub-group index 0 and the second at sub-group index 16. For srcLane = 4 the
192 first subdivision will access the value at sub-group index 4 and the second at sub-group index 20. */
193 auto actual_group = get_opportunistic_group();
194 std::uint32_t const w = static_cast<std::uint32_t>(width);
195 std::uint32_t const start_index = actual_group.get_local_linear_id() / w * w;
196 return sycl::select_from_group(actual_group, value, start_index + static_cast<std::uint32_t>(srcLane) % w);
197 }
198 };
199
200 template<typename TDim>
201 struct ShflUp<warp::WarpGenericSycl<TDim>>
202 {
203 template<typename T>
204 static auto shfl_up(
205 warp::WarpGenericSycl<TDim> const& /*warp*/,
206 T value,
207 std::uint32_t offset, /* must be the same for all work-items in the group */
208 std::int32_t width)
209 {
210 ALPAKA_SYCL_WARP_CHECK_INLINE;
211
212 auto actual_group = get_opportunistic_group();
213 std::uint32_t const w = static_cast<std::uint32_t>(width);
214 std::uint32_t const id = actual_group.get_local_linear_id();
215 std::uint32_t const start_index = id / w * w;
216 T result = sycl::shift_group_right(actual_group, value, offset);
217 if((id - start_index) < offset)
218 {
219 result = value;
220 }
221 return result;
222 }
223 };
224
225 template<typename TDim>
226 struct ShflDown<warp::WarpGenericSycl<TDim>>
227 {
228 template<typename T>
229 static auto shfl_down(
230 warp::WarpGenericSycl<TDim> const& /*warp*/,
231 T value,
232 std::uint32_t offset,
233 std::int32_t width)
234 {
235 ALPAKA_SYCL_WARP_CHECK_INLINE;
236
237 auto actual_group = get_opportunistic_group();
238 std::uint32_t const w = static_cast<std::uint32_t>(width);
239 std::uint32_t const id = actual_group.get_local_linear_id();
240 std::uint32_t const end_index = (id / w + 1) * w;
241 T result = sycl::shift_group_left(actual_group, value, offset);
242 if((id + offset) >= end_index)
243 {
244 result = value;
245 }
246 return result;
247 }
248 };
249
250 template<typename TDim>
251 struct ShflXor<warp::WarpGenericSycl<TDim>>
252 {
253 template<typename T>
254 static auto shfl_xor(
255 warp::WarpGenericSycl<TDim> const& /*warp*/,
256 T value,
257 std::int32_t mask,
258 std::int32_t width)
259 {
260 ALPAKA_SYCL_WARP_CHECK_INLINE;
261
262 auto actual_group = get_opportunistic_group();
263 std::uint32_t const w = static_cast<std::uint32_t>(width);
264 std::uint32_t const id = actual_group.get_local_linear_id();
265 std::uint32_t const start_index = id / w * w;
266 std::uint32_t const target_offset = (id % w) ^ static_cast<std::uint32_t>(mask);
267 return sycl::select_from_group(actual_group, value, target_offset < w ? start_index + target_offset : id);
268 }
269 };
270} // namespace alpaka::warp::trait
271
272# undef ALPAKA_SYCL_WARP_CHECK_INLINE
273
274#endif
#define ALPAKA_ASSERT_ACC(...)
ALPAKA_ASSERT_ACC is an assert-like macro.
Definition Assert.hpp:52
The warp traits.
Definition Traits.hpp:21
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto ballot(TWarp const &warp, std::int32_t predicate) -> typename TWarp::mask_type
Evaluates predicate for all non-exited threads in a warp and returns a 32- or 64-bit unsigned integer...
Definition Traits.hpp:194
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto all(TWarp const &warp, std::int32_t predicate) -> std::int32_t
Evaluates predicate for all active threads of the warp and returns non-zero if and only if predicate ...
Definition Traits.hpp:144
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_up(TWarp const &warp, T value, std::uint32_t offset, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane with lower ID relative to caller....
Definition Traits.hpp:266
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC constexpr auto getSizeUpperLimit() -> std::int32_t
If the warp size is available as a compile-time constant returns its value; otherwise returns an uppe...
Definition Traits.hpp:96
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto any(TWarp const &warp, std::int32_t predicate) -> std::int32_t
Evaluates predicate for all active threads of the warp and returns non-zero if and only if predicate ...
Definition Traits.hpp:167
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_down(TWarp const &warp, T value, std::uint32_t offset, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane with higher ID relative to caller....
Definition Traits.hpp:304
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto getSize(TWarp const &warp) -> std::int32_t
Returns warp size.
Definition Traits.hpp:73
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_xor(TWarp const &warp, T value, std::int32_t mask, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane based on bitwise XOR of own lane I...
Definition Traits.hpp:342
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto activemask(TWarp const &warp) -> typename TWarp::mask_type
Returns a 32- or 64-bit unsigned integer (depending on the accelerator) whose Nth bit is set if and o...
Definition Traits.hpp:121
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC constexpr auto getSizeCompileTime() -> std::int32_t
If the warp size is available as a compile-time constant returns its value; otherwise returns 0.
Definition Traits.hpp:84
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl(TWarp const &warp, T value, std::int32_t srcLane, std::int32_t width=0)
Exchange data between threads within a warp.
Definition Traits.hpp:228