32#ifdef ALPAKA_ACC_SYCL_ENABLED
35# pragma clang diagnostic push
36# pragma clang diagnostic ignored "-Wunused-lambda-capture"
37# pragma clang diagnostic ignored "-Wunused-parameter"
40# include <sycl/ext/oneapi/experimental/root_group.hpp>
41# include <sycl/ext/oneapi/properties/properties.hpp>
42# include <sycl/sycl.hpp>
44# define LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(sub_group_size) \
45 if constexpr(TCooperative) \
47 auto const kernel = [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
48 sycl::nd_item<TDim::value> work_item) \
50 auto acc = TAcc{item_elements, work_item, dyn_shared_accessor, st_shared_accessor}; \
51 std::apply([&](auto&&... args) { detail::checkKernelReturnType(k_func, acc, args...); }, k_args); \
53 [k_func, &acc](typename std::decay_t<TArgs> const&... args) { k_func(acc, args...); }, \
56 cgh.parallel_for<detail::SyclKernel<TKernelFnObj, TDim, TIdx, TArgs...>>( \
57 sycl::nd_range<TDim::value>{global_size, local_size}, \
58 detail::SyclRootSyncKernel<sub_group_size, TDim::value, std::remove_const_t<decltype(kernel)>>{ \
64 sycl::nd_range<TDim::value>{global_size, local_size}, \
65 [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
66 sycl::nd_item<TDim::value> work_item) [[sycl::reqd_sub_group_size(sub_group_size)]] \
68 auto acc = TAcc{item_elements, work_item, dyn_shared_accessor, st_shared_accessor}; \
69 std::apply([&](auto&&... args) { detail::checkKernelReturnType(k_func, acc, args...); }, k_args); \
71 [k_func, &acc](typename std::decay_t<TArgs> const&... args) { k_func(acc, args...); }, \
76# define LAUNCH_SYCL_KERNEL_WITH_DEFAULT_SUBGROUP_SIZE \
77 if constexpr(TCooperative) \
79 auto const kernel = [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
80 sycl::nd_item<TDim::value> work_item) \
82 auto acc = TAcc{item_elements, work_item, dyn_shared_accessor, st_shared_accessor}; \
83 std::apply([&](auto&&... args) { detail::checkKernelReturnType(k_func, acc, args...); }, k_args); \
85 [k_func, &acc](typename std::decay_t<TArgs> const&... args) { k_func(acc, args...); }, \
88 cgh.parallel_for<detail::SyclKernel<TKernelFnObj, TDim, TIdx, TArgs...>>( \
89 sycl::nd_range<TDim::value>{global_size, local_size}, \
90 detail::SyclRootSyncKernel<0, TDim::value, std::remove_const_t<decltype(kernel)>>{kernel}); \
95 sycl::nd_range<TDim::value>{global_size, local_size}, \
96 [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
97 sycl::nd_item<TDim::value> work_item) \
99 auto acc = TAcc{item_elements, work_item, dyn_shared_accessor, st_shared_accessor}; \
100 std::apply([&](auto&&... args) { detail::checkKernelReturnType(k_func, acc, args...); }, k_args); \
102 [k_func, &acc](typename std::decay_t<TArgs> const&... args) { k_func(acc, args...); }, \
107# define THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL \
108 throw std::runtime_error( \
109 "The SYCL targets do not support the sub-group size " + std::to_string(sub_group_size) \
110 + " required by the kernel on " + getAccName<TAcc>()); \
111 if constexpr(TCooperative) \
113 cgh.parallel_for<detail::SyclKernel<TKernelFnObj, TDim, TIdx, TArgs...>>( \
114 sycl::nd_range<TDim::value>{global_size, local_size}, \
115 [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
116 sycl::nd_item<TDim::value> work_item) {}); \
121 sycl::nd_range<TDim::value>{global_size, local_size}, \
122 [item_elements, dyn_shared_accessor, st_shared_accessor, k_func, k_args]( \
123 sycl::nd_item<TDim::value> work_item) {}); \
131 template<
typename TKernel,
typename TDim,
typename TIdx,
typename... TArgs>
136 template<std::
size_t TSubGroupSize,
int TDims,
typename TKernel>
137 struct SyclRootSyncKernel
141 auto operator()(sycl::nd_item<TDims> work_item)
const ->
void
146 auto get(sycl::ext::oneapi::experimental::properties_tag)
const
148 namespace syclex = sycl::ext::oneapi::experimental;
149 if constexpr(TSubGroupSize == 0)
150 return syclex::properties{syclex::use_root_sync};
152 return syclex::properties{syclex::use_root_sync, syclex::sub_group_size<TSubGroupSize>};
160 template<
typename TDim,
typename TIdx>
161 auto syclWorkGroupSize(Vec<TDim, TIdx>
const& group_items)
163 if constexpr(TDim::value == 1)
164 return sycl::range<1>{
static_cast<std::size_t
>(group_items[0])};
165 else if constexpr(TDim::value == 2)
166 return sycl::range<2>{
167 static_cast<std::size_t
>(group_items[0]),
168 static_cast<std::size_t
>(group_items[1])};
170 return sycl::range<3>{
171 static_cast<std::size_t
>(group_items[0]),
172 static_cast<std::size_t
>(group_items[1]),
173 static_cast<std::size_t
>(group_items[2])};
180 template<
typename TKernelName,
int TDims>
181 auto getMaxNumWorkGroupsSync(
182 sycl::queue
const& queue,
183 [[maybe_unused]] sycl::range<TDims>
const& workGroupSize,
184 [[maybe_unused]] std::size_t localMemBytes) -> std::size_t
186 auto const bundle = sycl::get_kernel_bundle<sycl::bundle_state::executable>(queue.get_context());
187 auto const kernel = bundle.template get_kernel<TKernelName>();
188# if ALPAKA_COMP_ICPX >= ALPAKA_VERSION_NUMBER(2025, 1, 0)
191 return kernel.template ext_oneapi_get_info<
192 sycl::ext::oneapi::experimental::info::kernel_queue_specific::max_num_work_groups>(
197 return kernel.template ext_oneapi_get_info<
198 sycl::ext::oneapi::experimental::info::kernel_queue_specific::max_num_work_group_sync>(queue);
209 typename TKernelFnObj,
212 class TaskKernelGenericSycl final :
public WorkDivMembers<TDim, TIdx>
215 static_assert(TDim::value > 0 && TDim::value <= 3,
"Invalid kernel dimensionality");
217 template<
typename TWorkDiv>
218 TaskKernelGenericSycl(TWorkDiv&& workDiv, TKernelFnObj
const& kernelFnObj, TArgs&&... args)
220 , m_kernelFnObj{kernelFnObj}
221 , m_args{
std::forward<TArgs>(args)...}
225 auto operator()(sycl::handler& cgh, sycl::queue
const& queue)
const ->
void
227 auto const work_groups = WorkDivMembers<TDim, TIdx>::m_gridBlockExtent;
228 auto const group_items = WorkDivMembers<TDim, TIdx>::m_blockThreadExtent;
229 auto const item_elements = WorkDivMembers<TDim, TIdx>::m_threadElemExtent;
231 auto const global_size = get_global_size(work_groups, group_items);
232 auto const local_size = get_local_size(group_items);
235 auto const dyn_shared_mem_bytes = std::max(
238 [&](std::decay_t<TArgs>
const&... args) {
239 return getBlockSharedMemDynSizeBytes<TAcc>(m_kernelFnObj, group_items, item_elements, args...);
243 auto dyn_shared_accessor = sycl::local_accessor<std::byte>{sycl::range<1>{dyn_shared_mem_bytes}, cgh};
246 constexpr auto st_shared_mem_bytes = detail::syclStaticSharedMemBytes;
247 auto st_shared_accessor = sycl::local_accessor<std::byte>{sycl::range<1>{st_shared_mem_bytes}, cgh};
250 auto k_func = m_kernelFnObj;
251 auto k_args = m_args;
253 constexpr std::size_t sub_group_size = trait::warpSize<TKernelFnObj, TAcc>;
259 if constexpr(std::is_same_v<TTag, TagCpuSycl> && TDim::value > 1 && sub_group_size > 0)
261 auto const innermost =
static_cast<std::size_t
>(group_items[TDim::value - 1]);
262 if(innermost % sub_group_size != 0)
264 throw std::runtime_error(
265 "On the SYCL CPU device, the innermost block extent (" + std::to_string(innermost)
266 +
") must be a multiple of the warp size (" + std::to_string(sub_group_size)
267 +
") because sub-groups are formed only along the innermost dimension");
271 bool supported =
false;
273 if constexpr(TCooperative)
277 if(!detail::syclCooperativeLaunch<TTag>(queue.get_device()))
279 throw std::runtime_error(
280 "The device " + queue.get_device().get_info<sycl::info::device::name>() +
" ("
281 + getAccName<TAcc>() +
") does not support cooperative kernels");
285# if ALPAKA_DEBUG >= ALPAKA_DEBUG_MINIMAL
286 if constexpr(TCooperative)
288 std::size_t
const maxWGs
289 = detail::getMaxNumWorkGroupsSync<detail::SyclKernel<TKernelFnObj, TDim, TIdx, TArgs...>>(
292 dyn_shared_mem_bytes + st_shared_mem_bytes);
293 if(
static_cast<std::size_t
>(work_groups.prod()) > maxWGs)
295 throw std::runtime_error(
296 "The number of requested blocks is larger than maximuma of the device for the kernel "
297 + std::string(core::demangled<TKernelFnObj>) +
"! Device: " + getAccName<TAcc>()
298 +
", requested: " + std::to_string(work_groups.prod())
299 +
", maximum allowed: " + std::to_string(maxWGs) +
". Use getMaxActiveBlocks().");
301# if ALPAKA_DEBUG >= ALPAKA_DEBUG_FULL
302 std::cout <<
"maxBlocksPerGrid for the " << core::demangled<TKernelFnObj> <<
": " << maxWGs
308 if constexpr(sub_group_size == 0)
311 LAUNCH_SYCL_KERNEL_WITH_DEFAULT_SUBGROUP_SIZE
316# if(SYCL_SUBGROUP_SIZE == 0)
318 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(sub_group_size)
322 if constexpr(sub_group_size == 4)
324# if(SYCL_SUBGROUP_SIZE & 4)
325 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(4)
329 THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL
334 if constexpr(sub_group_size == 8)
336# if(SYCL_SUBGROUP_SIZE & 8)
337 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(8)
341 THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL
346 if constexpr(sub_group_size == 16)
348# if(SYCL_SUBGROUP_SIZE & 16)
349 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(16)
353 THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL
358 if constexpr(sub_group_size == 32)
360# if(SYCL_SUBGROUP_SIZE & 32)
361 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(32)
365 THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL
370 if constexpr(sub_group_size == 64)
372# if(SYCL_SUBGROUP_SIZE & 64)
373 LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS(64)
377 THROW_AND_LAUNCH_EMPTY_SYCL_KERNEL
384 throw std::runtime_error(
385 "The SYCL targets do not support the sub-group size " + std::to_string(sub_group_size)
386 +
" required by the kernel on " + getAccName<TAcc>());
390 static constexpr auto is_sycl_task =
true;
392 static constexpr auto is_sycl_kernel =
true;
395 auto get_global_size(Vec<TDim, TIdx>
const& work_groups, Vec<TDim, TIdx>
const& group_items)
const
397 if constexpr(TDim::value == 1)
398 return sycl::range<1>{
static_cast<std::size_t
>(work_groups[0] * group_items[0])};
399 else if constexpr(TDim::value == 2)
400 return sycl::range<2>{
401 static_cast<std::size_t
>(work_groups[0] * group_items[0]),
402 static_cast<std::size_t
>(work_groups[1] * group_items[1])};
404 return sycl::range<3>{
405 static_cast<std::size_t
>(work_groups[0] * group_items[0]),
406 static_cast<std::size_t
>(work_groups[1] * group_items[1]),
407 static_cast<std::size_t
>(work_groups[2] * group_items[2])};
410 auto get_local_size(Vec<TDim, TIdx>
const& group_items)
const
412 return detail::syclWorkGroupSize(group_items);
416 TKernelFnObj m_kernelFnObj;
417 std::tuple<std::decay_t<TArgs>...> m_args;
422# if ALPAKA_COMP_CLANG
423# pragma clang diagnostic pop
429 template<
typename TAcc,
typename TDim,
typename TIdx,
typename TKernelFnObj,
typename... TArgs>
430 struct AccType<TaskKernelGenericSycl<TAcc, TDim, TIdx, TKernelFnObj, TArgs...>>
436 template<
typename TAcc,
typename TDim,
typename TIdx,
typename TKernelFnObj,
typename... TArgs>
437 struct DevType<TaskKernelGenericSycl<TAcc, TDim, TIdx, TKernelFnObj, TArgs...>>
439 using type =
typename DevType<TAcc>::type;
443 template<
typename TAcc,
typename TDim,
typename TIdx,
typename TKernelFnObj,
typename... TArgs>
444 struct PlatformType<TaskKernelGenericSycl<TAcc, TDim, TIdx, TKernelFnObj, TArgs...>>
446 using type =
typename PlatformType<TAcc>::type;
450 template<
typename TAcc,
typename TDim,
typename TIdx,
typename TKernelFnObj,
typename... TArgs>
451 struct DimType<TaskKernelGenericSycl<TAcc, TDim, TIdx, TKernelFnObj, TArgs...>>
457 template<
typename TAcc,
typename TDim,
typename TIdx,
typename TKernelFnObj,
typename... TArgs>
458 struct IdxType<TaskKernelGenericSycl<TAcc, TDim, TIdx, TKernelFnObj, TArgs...>>
470 template<concepts::Tag TTag,
typename TDev,
typename TDim,
typename TIdx,
typename TKernelFn,
typename... TArgs>
471 struct FunctionAttributes<AccGenericSycl<TTag, TDim, TIdx>, TDev, TKernelFn, TArgs...>
480 [[maybe_unused]] TKernelFn
const& kernelFn,
487 kernelFunctionAttributes.
maxThreadsPerBlock =
static_cast<int>(props.m_blockThreadCountMax);
488 return kernelFunctionAttributes;
493 template<
typename TAcc,
typename TKernelFnObj,
typename TTag,
typename TDim,
typename TIdx,
typename... TArgs>
494 struct MaxActiveBlocks<TAcc, DevGenericSycl<TTag>, TKernelFnObj, TDim, TIdx, TArgs...>
497 TKernelFnObj
const& kernelFnObj,
498 DevGenericSycl<TTag>
const& device,
501 TArgs
const&... args) ->
int
504 auto const dynSharedMemBytes = std::max(
506 static_cast<std::size_t
>(
510 std::move(device.getNativeHandle()
512 std::move(device.getNativeHandle().first),
513 {sycl::property::queue::enable_profiling{}, sycl::property::queue::in_order{}}};
515 std::size_t
const maxWGs
516 = detail::getMaxNumWorkGroupsSync<detail::SyclKernel<TKernelFnObj, TDim, TIdx, TArgs...>>(
518 detail::syclWorkGroupSize(blockThreadExtent),
519 dynSharedMemBytes + detail::syclStaticSharedMemBytes);
520 return static_cast<int>(maxWGs);
525# undef LAUNCH_SYCL_KERNEL_IF_SUBGROUP_SIZE_IS
#define ALPAKA_BLOCK_SHARED_DYN_MEMBER_ALLOC_KIB
The alpaka accelerator library.
ALPAKA_FN_HOST auto getAccDevProps(TDev const &dev) -> AccDevProps< Dim< TAcc >, Idx< TAcc > >
WorkDivMembers(alpaka::Vec< TDim, TIdx > const &gridBlockExtent, alpaka::Vec< TDim, TIdx > const &blockThreadExtent, alpaka::Vec< TDim, TIdx > const &elemExtent) -> WorkDivMembers< TDim, TIdx >
Deduction guide for the constructor which can be called without explicit template type parameters.
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_HOST_ACC auto getMaxActiveBlocks(TDev const &device, TKernelFnObj const &kernelFnObj, alpaka::Vec< TDim, TIdx > const &blockThreadExtent, alpaka::Vec< TDim, TIdx > const &threadElemExtent, TArgs const &... args) -> int
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_HOST_ACC auto getBlockSharedMemDynSizeBytes(TKernelFnObj const &kernelFnObj, Vec< TDim, Idx< TAcc > > const &blockThreadExtent, Vec< TDim, Idx< TAcc > > const &threadElemExtent, TArgs const &... args) -> std::size_t
Kernel function attributes struct. Attributes are filled by calling the API of the accelerator using ...
static ALPAKA_FN_HOST auto getFunctionAttributes(TDev const &dev, TKernelFnObj const &kernelFn, TArgs &&... args) -> alpaka::KernelFunctionAttributes