alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
Queue.hpp
Go to the documentation of this file.
1/* Copyright 2025 Simeon Ehrig, René Widera
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
15
16#if ALPAKA_LANG_ONEAPI
17
18# ifndef ALPAKA_SYCL_NUM_MAX_SHARED_MEMORY_ALLOCATIONS
19# define ALPAKA_SYCL_NUM_MAX_SHARED_MEMORY_ALLOCATIONS 32u
20# endif
21
22# ifndef SYCL_EXT_ONEAPI_MEMCPY2D
23# error \
24 "SYCL_EXT_ONEAPI_MEMCPY2D is not defined. Extension https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/supported/sycl_ext_oneapi_memcpy2d.asciidoc is required!"
25# endif
26
27namespace alpaka::onHost::internal
28{
29 template<typename T_Device, typename T_Dest, typename T_Extents>
31 struct Memset::Op<syclGeneric::Queue<T_Device>, T_Dest, T_Extents>
32 {
33 void operator()(syclGeneric::Queue<T_Device>& queue, auto&& dest, uint8_t byteValue, T_Extents const& extents)
34 const requires std::same_as<ALPAKA_TYPEOF(dest), T_Dest>
35 {
36 sycl::queue sycl_queue = queue.getNativeHandle();
37
38 // use always 64bit precision to avoid overflows in the pitch calculations
39 auto extentMd = pCast<size_t>(extents);
40 if(extentMd.product() == size_t{0u})
41 return;
42
43 auto destPitch = pCast<size_t>(onHost::getPitches(dest));
44 auto* destPtr = data(dest);
45
46 constexpr auto dim = alpaka::trait::getDim_v<T_Extents>;
47
48 sycl::event ev;
49
50 if constexpr(dim == 2u)
51 {
52 ev = sycl_queue.ext_oneapi_memset2d(
53 destPtr,
54 destPitch.y(),
55 byteValue,
56 extentMd.x() * sizeof(alpaka::trait::GetValueType_t<T_Dest>),
57 extentMd.y());
58 }
59 else if constexpr(dim >= 3u)
60 {
61 // dest must be contiguous memory after the 2 dimension
62 bool isContiguous = true;
63
64 /* Skip the fastest dimension.
65 * We need to check that we do not have padding between dimension 2->3 or higher.
66 * Padding in column or between rows is no problem because this is supported by 2d memcpy
67 */
68 for(uint32_t d = dim - 2u; d >= 1u; --d)
69 isContiguous = isContiguous && (extentMd[d] * destPitch[d] == destPitch[d - 1u]);
70
71 if(isContiguous)
72 {
73 /* If the memory is contiguous in the dimensions higher than 2 we can emulate the N-dimensional
74 * memset with a 2D memset by mapping the higher dimensions into y.
75 * This is more efficient than calling the sycl memset function multiple times.
76 */
77 alpaka::concepts::Vector<size_t, 2u> auto mappedExtentMd = extentMd.template rshrink<2u>();
78 // remove x dimension, fuse all other dimensions into the y component
79 mappedExtentMd.y() = extentMd.eraseBack().product();
80
81 ev = memset2D<alpaka::trait::GetValueType_t<T_Dest>>(
82 sycl_queue,
83 byteValue,
84 // 2D is nativ supported therefore we can handle the memset with a single call
85 ALPAKA_TYPEOF(mappedExtentMd)::fill(1u),
86 ALPAKA_TYPEOF(mappedExtentMd)::fill(0u),
87 mappedExtentMd,
88 destPtr,
89 destPitch.template rshrink<2u>());
90 }
91 else
92 {
93 // remove the 2 fast moving dimensions
94 auto repetitions = extentMd.template rshrink<dim - 2u>(dim - 3u);
95 auto destPitchJump = destPitch.template rshrink<dim - 2u>(dim - 3u);
96
97 ev = memset2D<alpaka::trait::GetValueType_t<T_Dest>>(
98 sycl_queue,
99 byteValue,
100 repetitions,
101 destPitchJump,
102 extentMd,
103 destPtr,
104 destPitch);
105 }
106 }
107
108 queue.setLastEvent(ev);
109 if(queue.isBlocking())
110 ev.wait_and_throw();
111 }
112
113 /** Memset which calls multiple times the memset2D.
114 *
115 * The memset method is repetitions times repeated and the destPtr is advanced each time by the corresponding
116 * pitches in bytes.
117 *
118 * @param repetitions how often memset2D should be called
119 * @param destPitchJump bytes vector with pitches required to jump to the next 2D block to memset for the
120 * destPtr. Dimension must be equal to repetitions.
121 * @param extentMd Extents to describe how many elements should be copied. Dimension should be equal to the
122 * original buffer/view dimension.
123 * @param destPitchesOriginal Original pitches of destPtr. Dimension should be equal to the original
124 * buffer/view dimension.
125 */
126 template<typename T_ValueType>
127 sycl::event memset2D(
128 sycl::queue& sycl_queue,
129 uint8_t byteValue,
130 alpaka::concepts::Vector<size_t> auto const& repetitions,
131 alpaka::concepts::Vector<size_t> auto const& destPitchJump,
132 alpaka::concepts::Vector<size_t> auto const& extentMd,
133 void* destPtr,
134 alpaka::concepts::Vector<size_t> auto const& destPitchesOriginal) const
135 {
136 static_assert(ALPAKA_TYPEOF(repetitions)::dim() == ALPAKA_TYPEOF(destPitchJump)::dim());
137 static_assert(ALPAKA_TYPEOF(extentMd)::dim() == ALPAKA_TYPEOF(destPitchesOriginal)::dim());
138
139 sycl::event ev;
141 repetitions,
142 [&](auto const& idx)
143 {
144 if(idx != repetitions - ALPAKA_TYPEOF(repetitions)::fill(1u))
145 {
146 // Sycl is allowed to optimize and not create events for each call.
147 sycl_queue.ext_oneapi_memset2d(
148 reinterpret_cast<uint8_t*>(destPtr) + (idx * destPitchJump).sum(),
149 destPitchesOriginal.y(),
150 byteValue,
151 extentMd.x() * sizeof(T_ValueType),
152 extentMd.y());
153 }
154 else
155 {
156 // the last call must create an event
157 ev = sycl_queue.ext_oneapi_memset2d(
158 reinterpret_cast<uint8_t*>(destPtr) + (idx * destPitchJump).sum(),
159 destPitchesOriginal.y(),
160 byteValue,
161 extentMd.x() * sizeof(T_ValueType),
162 extentMd.y());
163 }
164 });
165 return ev;
166 }
167 };
168
169 template<typename T_Device, typename T_Dest, typename T_Source, typename T_Extents>
171 struct internal::Memcpy::Op<syclGeneric::Queue<T_Device>, T_Dest, T_Source, T_Extents>
172 {
173 /** Perform data copy.
174 *
175 * To understand the usage of pitches to shift pointers within the implementation see
176 * https://alpaka3.readthedocs.io/en/latest/advanced/datastorage.html#pitches
177 */
178 void operator()(
179 syclGeneric::Queue<T_Device>& queue,
180 auto&& dest,
181 T_Source const& src,
182 T_Extents const& extents) const requires std::same_as<ALPAKA_TYPEOF(dest), T_Dest>
183 {
184 sycl::queue sycl_queue = queue.getNativeHandle();
185
186 // use always 64bit precision to avoid overflows in the pitch calculations
187 auto extentMd = pCast<size_t>(extents);
188 if(extentMd.product() == size_t{0u})
189 return;
190
191 auto* destPtr = data(dest);
192 auto destPitch = pCast<size_t>(onHost::getPitches(dest));
193 auto const* srcPtr = data(src);
194 auto srcPitch = pCast<size_t>(onHost::getPitches(src));
195
196 constexpr auto dim = alpaka::trait::getDim_v<T_Extents>;
197
198 sycl::event ev;
199
200 if constexpr(dim == 2u)
201 {
202 ev = sycl_queue.ext_oneapi_memcpy2d(
203 destPtr,
204 destPitch.y(),
205 srcPtr,
206 srcPitch.y(),
207 extentMd.x() * sizeof(alpaka::trait::GetValueType_t<T_Dest>),
208 extentMd.y());
209 }
210 else if constexpr(dim >= 3u)
211 {
212 // Both src and dest must be contiguous memory after the 2 dimension
213 bool isContiguous = true;
214
215 /* Skip the fastest dimension.
216 * We need to check that we do not have padding between dimension 2->3 or higher.
217 * Padding in column or between rows is no problem because this is supported by 2d memcpy
218 */
219 for(uint32_t d = dim - 2u; d >= 1u; --d)
220 {
221 isContiguous = isContiguous && (extentMd[d] * destPitch[d] == destPitch[d - 1u])
222 && (extentMd[d] * srcPitch[d] == srcPitch[d - 1u]);
223 }
224
225 if(isContiguous)
226 {
227 /* If the memory is contiguous in the dimensions higher than 2 we can emulate the N-dimensional
228 * copy with a 2D memcpy by mapping the higher dimensions into y.
229 * This is more efficient than calling the sycl memcopy function multiple times.
230 */
231 alpaka::concepts::Vector<size_t, 2u> auto mappedExtentMd = extentMd.template rshrink<2u>();
232 // remove x dimension, fuse all other dimensions into the y component
233 mappedExtentMd.y() = extentMd.eraseBack().product();
234 using VecIdxType = ALPAKA_TYPEOF(mappedExtentMd);
235
236 ev = memcopy2D<alpaka::trait::GetValueType_t<T_Dest>>(
237 sycl_queue,
238 // 2D is nativ supported therefore we can handle the memcpy with a single call
239 VecIdxType::fill(1u),
240 VecIdxType::fill(0u),
241 VecIdxType::fill(0u),
242 mappedExtentMd,
243 destPtr,
244 destPitch.template rshrink<2u>(),
245 srcPtr,
246 srcPitch.template rshrink<2u>());
247 }
248 else
249 {
250 // remove the 2 fast moving dimensions
251 auto repetitions = extentMd.template rshrink<dim - 2u>(dim - 3u);
252 auto srcPitchJump = srcPitch.template rshrink<dim - 2u>(dim - 3u);
253 auto destPitchJump = destPitch.template rshrink<dim - 2u>(dim - 3u);
254
255 ev = memcopy2D<alpaka::trait::GetValueType_t<T_Dest>>(
256 sycl_queue,
257 repetitions,
258 destPitchJump,
259 srcPitchJump,
260 extentMd,
261 destPtr,
262 destPitch,
263 srcPtr,
264 srcPitch);
265 }
266 }
267
268 queue.setLastEvent(ev);
269 if(queue.isBlocking())
270 ev.wait_and_throw();
271 }
272
273 /** Memcopy which calls multiple times the 2D memcpy.
274 *
275 * The copy method is repetitions times repeated and the srcPtr and destPtr is advanced each time by the
276 * corresponding pitches in bytes.
277 *
278 * @param repetitions how often the 2D memcpy should be called
279 * @param destPitchJump bytes vector with pitches required to jump to the next 2D block to copy for the
280 * destPtr. Dimension must be equal to repetitions.
281 * @param srcPitchJump bytes vector with pitches required to jump to the next 2D block to copy for the srcPtr.
282 * Dimension must be equal to repetitions.
283 * @param extentMd Extents to describe how many elements should be copied. Dimension should be equal to the
284 * original buffer/view dimension.
285 * @param destPitchesOriginal Original pitches of destPtr. Dimension should be equal to the original
286 * buffer/view dimension.
287 * @param srcPitchesOriginal Original pitches of srcPtr. Dimension should be equal to the original buffer/view
288 * dimension.
289 */
290 template<typename T_ValueType>
291 sycl::event memcopy2D(
292 sycl::queue& sycl_queue,
293 alpaka::concepts::Vector<size_t> auto const& repetitions,
294 alpaka::concepts::Vector<size_t> auto const& destPitchJump,
295 alpaka::concepts::Vector<size_t> auto const& srcPitchJump,
296 alpaka::concepts::Vector<size_t> auto const& extentMd,
297 void* destPtr,
298 alpaka::concepts::Vector<size_t> auto const& destPitchesOriginal,
299 void const* srcPtr,
300 alpaka::concepts::Vector<size_t> auto const& srcPitchesOriginal) const
301 {
302 static_assert(
303 ALPAKA_TYPEOF(repetitions)::dim() == ALPAKA_TYPEOF(destPitchJump)::dim()
304 && ALPAKA_TYPEOF(repetitions)::dim() == ALPAKA_TYPEOF(srcPitchJump)::dim());
305 static_assert(
306 ALPAKA_TYPEOF(extentMd)::dim() == ALPAKA_TYPEOF(destPitchesOriginal)::dim()
307 && ALPAKA_TYPEOF(extentMd)::dim() == ALPAKA_TYPEOF(srcPitchesOriginal)::dim());
308
309 sycl::event ev;
311 repetitions,
312 [&](auto const& idx)
313 {
314 if(idx != repetitions - ALPAKA_TYPEOF(repetitions)::fill(1u))
315 {
316 // Sycl is allowed to optimize and not create events for each call.
317 sycl_queue.ext_oneapi_memcpy2d(
318 reinterpret_cast<uint8_t*>(destPtr) + (idx * destPitchJump).sum(),
319 destPitchesOriginal.y(),
320 reinterpret_cast<uint8_t const*>(srcPtr) + (idx * srcPitchJump).sum(),
321 srcPitchesOriginal.y(),
322 extentMd.x() * sizeof(T_ValueType),
323 extentMd.y());
324 }
325 else
326 {
327 // the last call must create an event
328 ev = sycl_queue.ext_oneapi_memcpy2d(
329 reinterpret_cast<uint8_t*>(destPtr) + (idx * destPitchJump).sum(),
330 destPitchesOriginal.y(),
331 reinterpret_cast<uint8_t const*>(srcPtr) + (idx * srcPitchJump).sum(),
332 srcPitchesOriginal.y(),
333 extentMd.x() * sizeof(T_ValueType),
334 extentMd.y());
335 }
336 });
337
338 return ev;
339 }
340 };
341
342 // copy to device global memory
343 template<typename T_Device, typename T_Source, typename T_Storage, typename T>
344 struct internal::MemcpyDeviceGlobal::
345 Op<syclGeneric::Queue<T_Device>, onAcc::internal::GlobalDeviceMemoryWrapper<T_Storage, T>, T_Source>
346 {
347 void operator()(
348 syclGeneric::Queue<T_Device>& queue,
349 onAcc::internal::GlobalDeviceMemoryWrapper<T_Storage, T> dest,
350 auto&& source) const
351 {
353 sycl::queue sycl_queue = queue.getNativeHandle();
354 void const* srcPtr{nullptr};
355 if constexpr(std::is_pointer_v<ALPAKA_TYPEOF(source)>)
356 srcPtr = source;
357 else
358 srcPtr = toVoidPtr(alpaka::onHost::data(source));
359 sycl::event ev = sycl_queue.memcpy(dest.getHandle(alpaka::api::oneApi), srcPtr);
360 queue.setLastEvent(ev);
361 if(queue.isBlocking())
362 ev.wait_and_throw();
363 }
364 };
365
366 // copy from device global memory
367 template<typename T_Device, typename T_Dest, typename T_Storage, typename T>
368 struct internal::MemcpyDeviceGlobal::
369 Op<syclGeneric::Queue<T_Device>, T_Dest, onAcc::internal::GlobalDeviceMemoryWrapper<T_Storage, T>>
370 {
371 void operator()(
372 syclGeneric::Queue<T_Device>& queue,
373 auto&& dest,
374 onAcc::internal::GlobalDeviceMemoryWrapper<T_Storage, T> source) const
375 {
377 sycl::queue sycl_queue = queue.getNativeHandle();
378 void* destPtr{nullptr};
379 if constexpr(std::is_pointer_v<ALPAKA_TYPEOF(dest)>)
380 destPtr = dest;
381 else
382 destPtr = toVoidPtr(alpaka::onHost::data(dest));
383 sycl::event ev = sycl_queue.memcpy(destPtr, source.getHandle(alpaka::api::oneApi));
384 queue.setLastEvent(ev);
385 if(queue.isBlocking())
386 ev.wait_and_throw();
387 }
388 };
389
390 template<typename T_Device, typename T_Dest, typename T_Value, typename T_Extents>
392 struct internal::Fill::Op<syclGeneric::Queue<T_Device>, T_Dest, T_Value, T_Extents>
393 {
394 void operator()(
395 syclGeneric::Queue<T_Device>& queue,
396 auto&& dest,
397 T_Value elementValue,
398 T_Extents const& extents) const
399 requires std::same_as<ALPAKA_TYPEOF(dest), T_Dest>
400 && std::same_as<alpaka::trait::GetValueType_t<ALPAKA_TYPEOF(dest)>, T_Value>
401 {
402 // avoid that we pass a SharedBuffer and convert non alpaka data views
403 auto dataView = makeView(dest);
404
405 alpaka::internal::generic::fill(
406 queue,
407 defaultExecutor(getDevice(queue)),
408 dataView.getSubView(extents),
409 elementValue);
410 }
411 };
412
413 namespace detail
414 {
415 template<alpaka::concepts::Vector TVec>
416 inline constexpr auto vecToSyclRange(TVec vec)
417 {
418 constexpr auto dim = std::decay_t<TVec>::dim();
419 return [&vec]<auto... I>(std::index_sequence<I...>)
420 // TODO: check if this is the correct order
421 { return sycl::range<dim>(vec[I]...); }(std::make_index_sequence<dim>{});
422 };
423
424 template<alpaka::concepts::Vector T_NumBlocks, alpaka::concepts::Vector T_NumThreads>
425 struct OptimizedThreadSpec
426 {
427 using NumBlocksVecType = typename T_NumBlocks::UniVec;
428 using NumThreadsVecType = T_NumThreads;
429
430 static consteval uint32_t dim()
431 {
432 return T_NumThreads::dim();
433 }
434
435 constexpr OptimizedThreadSpec(T_NumBlocks const&, T_NumThreads const&)
436 {
437 }
438 };
439
440 /** provides the sycl worker description
441 *
442 * @return A pair of the sycl nd range and an optimized thread spec. The thread spec is not holding any data
443 * for dimension smaller equal to 3u
444 */
445 template<onHost::concepts::ThreadSpec T_ThreadSpec>
446 inline constexpr auto getWorkerDescription(T_ThreadSpec const& threadSpec)
447 {
448 constexpr uint32_t dim = T_ThreadSpec::dim();
449 // dimension of the sycl nd range
450 constexpr uint32_t syclDim = dim >= 4u ? 1u : dim;
451
452 sycl::nd_range<syclDim> gridRange;
453
454 if constexpr(T_ThreadSpec::dim() >= 4u)
455 {
456 gridRange = sycl::nd_range<syclDim>{
457 (threadSpec.getNumBlocks() * threadSpec.getNumThreads()).product(),
458 threadSpec.getNumThreads().product()};
459 }
460 else
461 {
462 gridRange = sycl::nd_range<T_ThreadSpec::dim()>{
463 detail::vecToSyclRange(threadSpec.getNumBlocks() * threadSpec.getNumThreads()),
464 detail::vecToSyclRange(threadSpec.getNumThreads())};
465 }
466
467 using ThreadSpecType = std::conditional_t<
468 dim >= 4u,
469 ALPAKA_TYPEOF(threadSpec),
470 detail::OptimizedThreadSpec<
471 typename ALPAKA_TYPEOF(threadSpec)::NumBlocksVecType,
472 typename ALPAKA_TYPEOF(threadSpec)::NumThreadsVecType>>;
473 // thread spec which is only holding data if the dimension is larger than 3u
474 auto optimizedThreadSpec = ThreadSpecType(threadSpec.getNumBlocks(), threadSpec.getNumThreads());
475 return std::make_pair(gridRange, optimizedThreadSpec);
476 }
477
478 /** Generate the kernel with the given warp size.
479 *
480 * @tparam T_dim number of dimension of the kernel
481 * @tparam T_warpSize requested warp size
482 * @tparam T_isValid 0u means it is not valid, else it is valid and a kernel is generated
483 */
484 template<uint32_t T_dim, uint32_t T_warpSize, uint32_t T_isValid>
485 struct EnqueueKernelWithWarpSize
486 {
487 static void call(
488 sycl::handler& cgh,
489 auto gridRange,
490 auto const& kernelBundle,
491 auto const& st_shared_accessor,
492 auto const& dyn_shared_accessor,
493 auto const& optimizedThreadSpec,
494 auto... args)
495 {
496 cgh.parallel_for(
497 gridRange,
498 [kernelBundle, st_shared_accessor, dyn_shared_accessor, optimizedThreadSpec, args...](
499 sycl::nd_item<T_dim> work_item) [[sycl::reqd_sub_group_size(T_warpSize)]]
500 {
501 onAcc::oneApi::StaticSharedMemory ssm(st_shared_accessor);
502 onAcc::syclGeneric::DynamicSharedMemory dsm(dyn_shared_accessor);
503
504 static_assert(T_dim > 0);
505 static_assert(T_dim <= 3, "more the 3 dimensions are not supported");
506 auto acc = onAcc::Acc{Dict{
507 DictEntry(layer::block, onAcc::syclGeneric::BlockLayer{work_item, optimizedThreadSpec}),
508 DictEntry(layer::thread, onAcc::syclGeneric::ThreadLayer{work_item, optimizedThreadSpec}),
509 DictEntry(action::threadBlockSync, onAcc::syclGeneric::Sync{work_item}),
510 DictEntry(layer::shared, std::ref(ssm)),
511 DictEntry(layer::dynShared, std::ref(dsm)),
512 DictEntry(object::dynSharedMemBytes, dsm.byte_size()),
513 args...}};
514
515 kernelBundle(acc);
516 });
517 }
518 };
519
520 template<uint32_t T_dim, uint32_t T_warpSize>
521 struct EnqueueKernelWithWarpSize<T_dim, T_warpSize, 0u>
522 {
523 static void call(
524 [[maybe_unused]] sycl::handler& cgh,
525 [[maybe_unused]] auto gridRange,
526 [[maybe_unused]] auto const& kernelBundle,
527 [[maybe_unused]] auto const& st_shared_accessor,
528 [[maybe_unused]] auto const& dyn_shared_accessor,
529 [[maybe_unused]] auto const& optimizedThreadSpec,
530 [[maybe_unused]] auto... args)
531 {
532 printf(
533 "Dynamic evaluated warp size on host does not match the compile time warp size ( macro "
534 "ALPAKA_SYCL_SUBGROUP_SIZE) evaluated in the "
535 "kernel. Update the definition of ALPAKA_SYCL_SUBGROUP_SIZE section and check the trait "
536 "Warpsize::Dispatch<>.");
537 abort();
538 }
539 };
540 } // namespace detail
541
542 template<
543 typename T_Device,
545 alpaka::concepts::Vector T_NumBlocks,
546 alpaka::concepts::Vector T_NumThreads,
547 alpaka::concepts::KernelBundle T_KernelBundle>
548 struct Enqueue::
549 Kernel<syclGeneric::Queue<T_Device>, ThreadSpec<T_NumBlocks, T_NumThreads, T_Executor>, T_KernelBundle>
550 {
551 void operator()(
552 syclGeneric::Queue<T_Device>& queue,
553 ThreadSpec<T_NumBlocks, T_NumThreads, T_Executor> const& threadSpec,
554 T_KernelBundle const& kernelBundle) const
555 {
556 static_assert(
557 ALPAKA_TYPEOF(threadSpec)::getExecutor() != exec::anyExecutor,
558 "'exec::anyExecutor' can not be used to enqueue an kernel.");
559 ALPAKA_LOG_FUNCTION(onHost::logger::kernel + onHost::logger::queue);
560
561 constexpr auto st_shared_mem_bytes = onAcc::oneApi::StaticSharedMemory::sizeLookupBufferInBytes(
562 ALPAKA_SYCL_NUM_MAX_SHARED_MEMORY_ALLOCATIONS);
563 // allocate dynamic shared memory -- needs at least 1 byte to make the Xilinx Runtime happy
564 u_int32_t blockDynSharedMemBytes
565 = std::max(u_int32_t(1), onHost::getDynSharedMemBytes(threadSpec, kernelBundle));
566 assert(
567 st_shared_mem_bytes + blockDynSharedMemBytes
568 <= queue.m_device->getNativeHandle().first.template get_info<sycl::info::device::local_mem_size>());
569
570 sycl::event ev = queue.dispatchWarpSize(
571 [&](auto warpSize) requires std::same_as<
572 std::integral_constant<
573 typename ALPAKA_TYPEOF(warpSize)::value_type,
574 ALPAKA_TYPEOF(warpSize)::value>,
575 ALPAKA_TYPEOF(warpSize)>
576 {
577 return queue.m_queue.submit(
578 [warpSize, threadSpec, kernelBundle, blockDynSharedMemBytes](sycl::handler& cgh)
579 {
580 using ApiType = decltype(getApi(queue));
581 using DeviceKindType = ALPAKA_TYPEOF(getDeviceKind(queue));
582
583 auto st_shared_accessor
584 = sycl::local_accessor<std::byte>{sycl::range<1>{st_shared_mem_bytes}, cgh};
585
586 auto dyn_shared_accessor
587 = sycl::local_accessor<std::byte>{sycl::range<1>{blockDynSharedMemBytes}, cgh};
588
589 auto workerDesc = detail::getWorkerDescription(threadSpec);
590 auto optimizedThreadSpec = workerDesc.second;
591 constexpr uint32_t syclDim = workerDesc.first.dimensions;
592
593 constexpr uint32_t w = ALPAKA_TYPEOF(warpSize)::value;
594 detail::EnqueueKernelWithWarpSize<syclDim, w, ALPAKA_SYCL_SUBGROUP_SIZE & w>::call(
595 cgh,
596 workerDesc.first,
597 kernelBundle,
598 st_shared_accessor,
599 dyn_shared_accessor,
600 optimizedThreadSpec,
601 DictEntry(object::api, ApiType{}),
602 DictEntry(object::deviceKind, DeviceKindType{}),
603 DictEntry(object::exec, T_Executor{}),
604 DictEntry(object::launchedWidthFrameSpec, std::bool_constant<false>{}),
605 DictEntry(object::warpSize, warpSize));
606 });
607 });
608
609 queue.setLastEvent(ev);
610 if(queue.isBlocking())
611 ev.wait_and_throw();
612 }
613 };
614
615 template<
616 typename T_Device,
618 alpaka::concepts::Vector T_NumFrames,
619 alpaka::concepts::Vector T_FrameExtents,
620 alpaka::concepts::KernelBundle T_KernelBundle>
621 struct Enqueue::
622 Kernel<syclGeneric::Queue<T_Device>, FrameSpec<T_NumFrames, T_FrameExtents, T_Executor>, T_KernelBundle>
623 {
624 void operator()(
625 syclGeneric::Queue<T_Device>& queue,
626 FrameSpec<T_NumFrames, T_FrameExtents, T_Executor> const& frameSpec,
627 T_KernelBundle const& kernelBundle) const
628 {
629 static_assert(
630 ALPAKA_TYPEOF(frameSpec)::getExecutor() != exec::anyExecutor,
631 "'exec::anyExecutor' can not be used to enqueue an kernel.");
632 ALPAKA_LOG_FUNCTION(onHost::logger::kernel + onHost::logger::queue);
633
634 auto const threadBlocking = internal::adjustThreadSpec(*queue.m_device.get(), frameSpec, kernelBundle);
635
636 constexpr auto st_shared_mem_bytes = onAcc::oneApi::StaticSharedMemory::sizeLookupBufferInBytes(
637 ALPAKA_SYCL_NUM_MAX_SHARED_MEMORY_ALLOCATIONS);
638
639 // allocate dynamic shared memory -- needs at least 1 byte to make the Xilinx Runtime happy
640 u_int32_t blockDynSharedMemBytes
641 = std::max(u_int32_t(1), onHost::getDynSharedMemBytes(threadBlocking, kernelBundle));
642
643 assert(
644 st_shared_mem_bytes + blockDynSharedMemBytes
645 <= queue.m_device->getNativeHandle().first.template get_info<sycl::info::device::local_mem_size>());
646
647 sycl::event ev = queue.dispatchWarpSize(
648 [&](auto warpSize) requires std::same_as<
649 std::integral_constant<
650 typename ALPAKA_TYPEOF(warpSize)::value_type,
651 ALPAKA_TYPEOF(warpSize)::value>,
652 ALPAKA_TYPEOF(warpSize)>
653 {
654 return queue.m_queue.submit(
655 [warpSize, threadBlocking, kernelBundle, blockDynSharedMemBytes](sycl::handler& cgh)
656 {
657 using ApiType = decltype(getApi(queue));
658 using DeviceKindType = ALPAKA_TYPEOF(getDeviceKind(queue));
659 auto st_shared_accessor
660 = sycl::local_accessor<std::byte>{sycl::range<1>{st_shared_mem_bytes}, cgh};
661 auto dyn_shared_accessor
662 = sycl::local_accessor<std::byte>{sycl::range<1>{blockDynSharedMemBytes}, cgh};
663
664 auto workerDesc = detail::getWorkerDescription(threadBlocking);
665 auto optimizedThreadSpec = workerDesc.second;
666 constexpr uint32_t syclDim = workerDesc.first.dimensions;
667
668 constexpr uint32_t w = ALPAKA_TYPEOF(warpSize)::value;
669
670 detail::EnqueueKernelWithWarpSize<syclDim, w, ALPAKA_SYCL_SUBGROUP_SIZE & w>::call(
671 cgh,
672 workerDesc.first,
673 kernelBundle,
674 st_shared_accessor,
675 dyn_shared_accessor,
676 optimizedThreadSpec,
677 DictEntry(object::api, ApiType{}),
678 DictEntry(object::deviceKind, DeviceKindType{}),
679 DictEntry(object::exec, T_Executor{}),
680 DictEntry(object::launchedWidthFrameSpec, std::bool_constant<true>{}),
681 DictEntry(object::warpSize, warpSize));
682 });
683 });
684 if(queue.isBlocking())
685 ev.wait_and_throw();
686 }
687 };
688
689} // namespace alpaka::onHost::internal
690
691#endif
#define ALPAKA_TYPEOF(...)
Get the type of instance.
Definition common.hpp:154
Concept to check for an executor.
Definition trait.hpp:133
Concept to check if a type is a KernelBundle.
Concept to check if a type is a vector.
Definition Vec.hpp:54
#define ALPAKA_LOG_FUNCTION(logLvl)
Log the entry and exit of a scope.
Definition logger.hpp:95
constexpr auto oneApi
Definition Api.hpp:29
constexpr AnyExecutor anyExecutor
Automatic executor selection.
Definition executor.hpp:33
auto ndLoopIncIdx(TExtentVec &idx, TExtentVec const &extent, TFnObj const &f) -> void
Loops over an n-dimensional iteration index variable calling f(idx, args...) for each iteration....
Definition NdLoop.hpp:73
constexpr auto queue
Definition lvl.hpp:127
constexpr auto memory
Definition lvl.hpp:112
constexpr auto defaultExecutor(internal::concepts::DeviceHandle auto deviceHandle)
Select a default executor for the given device.
Definition trait.hpp:169
void fill(Queue< T_Device, T_QueueKind > const &queue, auto &&dest, T_Value elementValue)
fill memory element wise
Definition Queue.hpp:366
decltype(auto) data(auto &&any)
pointer to data of an object
decltype(auto) getPitches(auto &&any)
Object pitches.
Definition interface.hpp:55
typename GetValueType< T >::type GetValueType_t
Definition trait.hpp:65
constexpr uint32_t getDim_v
Definition trait.hpp:41
constexpr decltype(auto) getExecutor(auto &&any)
Get the executor associated with an object.
Definition interface.hpp:23
auto * toVoidPtr(T inPtr)
Cast a pointer that may or may not point to volatile memory to a (void*) or (void const*).
Definition util.hpp:34
constexpr decltype(auto) getDeviceKind(auto &&any)
Get the device type of an object.
Definition interface.hpp:78
constexpr decltype(auto) getApi(auto &&any)
Get the API an object depends on.
Definition interface.hpp:42
constexpr auto makeView(auto &&anyWithApi, T_ValueType *pointer, concepts::Vector auto const &extents, T_MemAlignment const memAlignment=T_MemAlignment{})
Definition View.hpp:37
constexpr decltype(auto) pCast(auto &&input)
Performs a static_cast on the storage type of combined data type.
Definition cast.hpp:48