alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
Queue.hpp
Go to the documentation of this file.
1/* Copyright 2024 René Widera
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
7#include "Handle.hpp"
9#include "alpaka/executor.hpp"
15
16#include <memory>
17
18namespace alpaka::onHost
19{
20 template<alpaka::concepts::Api T_Api, alpaka::concepts::DeviceKind T_DeviceKind>
21 struct Device;
22
23 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
24 struct Queue;
25
26 template<
30 struct Queue<Device<T_Api, T_DeviceKind>, T_QueueKind>
31 {
32 private:
35 internal::MakeQueue::Op<ALPAKA_TYPEOF(*std::declval<DeviceInterface>().get()), T_QueueKind>{}(
36 *std::declval<DeviceInterface>().get(),
37 T_QueueKind{}));
38
40
41 public:
42 using element_type = typename QueueHandle::element_type;
43
44 template<typename T_Queue>
45 Queue(Handle<T_Queue>&& queue, T_QueueKind) : m_queue{std::forward<Handle<T_Queue>>(queue)}
46 {
47 }
48
49 auto* get() const
50 {
51 return m_queue.get();
52 }
53
54 constexpr alpaka::concepts::Api auto getApi() const
55 {
56 return alpaka::internal::getApi(*m_queue.get());
57 }
58
60 {
61 return T_QueueKind{};
62 }
63
65 {
67 }
68
69 void _()
70 {
72 }
73
74 std::string getName() const
75 {
76 return alpaka::internal::GetName::Op<std::decay_t<decltype(*m_queue.get())>>{}(*m_queue.get());
77 }
78
79 [[nodiscard]] auto getNativeHandle() const
80 {
82 }
83
84 bool operator==(Queue const& other) const
85 {
86 return this->get() == other.get();
87 }
88
89 bool operator!=(Queue const& other) const
90 {
91 return this->get() != other.get();
92 }
93
94 /** Get the device of this queue.
95 *
96 * @return The device of this queue.
97 */
98 auto getDevice() const
99 {
101 }
102
103 /** Enqueue a kernel functor to a queue.
104 *
105 * @param launchCfg Thread or frame specification which provides a chunked description of the thread or
106 * frame index domain.
107 * @param kernelBundle The compute kernel and its arguments.
108 */
110 onHost::concepts::ThreadOrFrameSpec auto const& launchCfg,
111 alpaka::concepts::KernelBundle auto const& kernelBundle) const
112 {
113 if constexpr(
114 isFrameSpec_v<ALPAKA_TYPEOF(launchCfg)>
116 {
117 FrameSpec frameSpecWithExecutor = FrameSpec{
118 launchCfg.getNumFrames(),
119 launchCfg.getFrameExtents(),
121 internal::enqueue(*m_queue.get(), frameSpecWithExecutor, kernelBundle);
122 }
123 else if constexpr(
124 isThreadSpec_v<ALPAKA_TYPEOF(launchCfg)>
126 {
127 ThreadSpec threadSpecWithExecutor = ThreadSpec{
128 launchCfg.getNumBlocks(),
129 launchCfg.getNumThreads(),
131 internal::enqueue(*m_queue.get(), threadSpecWithExecutor, kernelBundle);
132 }
133 else
134 {
135 internal::enqueue(*m_queue.get(), launchCfg, kernelBundle);
136 }
137 }
138
139 /** Enqueue a kernel functor to a queue.
140 *
141 * @param launchCfg Thread or frame specification which provides a chunked description of the thread or
142 * frame index domain.
143 * @param f The compute kernel functor.
144 * @param args Arguments passed to the kernel functor.
145 */
146 void enqueue(onHost::concepts::ThreadOrFrameSpec auto const& launchCfg, auto const& f, auto&&... args) const
147 {
149 }
150
151 /** Enqueue an operation which is executed on the host side.
152 *
153 * @attention Do NOT enqueue a task which captures the queue internally to keep the queue alive, this could
154 * lead into deadlocks. Do NOT capture @see MangedView because view actions could perform blocking operations
155 * e.g. onHost::wait() in the destructor which could lead to deadlocks too.
156 *
157 * @param task Task to be executed on the host side.
158 */
159 void enqueueHostFn(auto const& task) const
160 {
162 }
163
164 /** Enqueue an operation which is executed asynchronously on the host side
165 *
166 * The enqueued operation will be started after all preceding tasks in the queue, but it may run after
167 * subsequent tasks in the queue. Because this task is asynchronous, it may contain vendor library functions,
168 * which may not be valid in an `enqueueHostFn` task.
169 *
170 * @param task Task to be executed asynchronously on the host side.
171 */
172 void enqueueHostFnDeferred(auto const& task) const
173 {
175 *m_queue.get(),
176 task);
177 }
178
179 /** Enqueue a native Api call.
180 *
181 * To keep the queue behavior e.g. blocking, non-blocking consistent in an application even if a native
182 * function is called it is required that alpaka knows about the function call and how to observe when the
183 * function is finished.
184 * By registering the native function API call alpaka can handle it.
185 * Typically, this method is used to call vendor functions, e.g. NVIDIA thrust.
186 *
187 * @attention fn should not capture arguments by reference because the execution can be deferred.
188 *
189 * @param fn A callable with the native queue handle for the corresponding API as only argument. The return
190 * value must be 'void' except for OneApi where a 'sycl::event' of the last function call must be returned.
191 * The native handle of the queue is passed to fn: HIP/CUDA get the stream, OneApi a sycl::queue and host gets
192 * the alpaka internal index of the queue.
193 */
194 void enqueueNativeFn(auto const& fn) const
195 {
197 }
198
199 /** Enqueue an event
200 *
201 * The event will be signaled after all preceding operations in the queue are finished.
202 *
203 * @param event Event that is to be enqueue in the queue of operations.
204 */
205 void enqueue(Event<Device<T_Api, T_DeviceKind>> const& event) const
206 {
208 *m_queue.get(),
209 *event.get());
210 }
211
212 /** Wait until all operations in this queue are finished.
213 *
214 * The caller will be blocked until all previously queued operations have been completed.
215 */
216 void waitFor(Event<Device<T_Api, T_DeviceKind>> const& event) const
217 {
218 internal::waitFor(*m_queue.get(), *event.get());
219 }
220
221 /** Checks if the queue does not have any enqueued work to process.
222 *
223 * @attention: If you enqueue work outside alpaka by using the native handle of the queue, this function is
224 * maybe not seeing this tasks and can return true even if there are unfinished tasks.
225 * If you need the guarantee that all tasks, even enqueued tasks outside alpaka are finished you should use
226 * onHost::wait(alpaka::concepts::HasGet auto&).
227 *
228 * @return true if there are no unfinished tasks in the queue, else false.
229 */
230 bool isEmpty() const
231 {
232 return internal::isQueueEmpty(*m_queue.get());
233 }
234 };
235
236 template<typename T_Queue, alpaka::concepts::QueueKind T_QueueKind>
237 Queue(Handle<T_Queue>&&, T_QueueKind) -> Queue<
238 Device<
239 ALPAKA_TYPEOF(alpaka::internal::getApi(std::declval<T_Queue>())),
240 ALPAKA_TYPEOF(alpaka::internal::getDeviceKind(std::declval<T_Queue>()))>,
241 T_QueueKind>;
242
243 /** @{
244 * @name Memory modifiers
245 *
246 * @attention For input/output memory the caller should ensure that the memory is valid until the operation is
247 * completed not until the execution handle is given back because alpaka is not extending the life-time until
248 * the operation is finished.
249 */
250 /** copy data byte wise from one to another container
251 *
252 * @param queue the copy will be executed after all previous work in this queue is finished
253 * @param[in,out] dest can be a container/view where the data should be written to
254 * @param[in] source can be a container/view from which the data will be copied
255 */
256 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
257 inline void memcpy(Queue<T_Device, T_QueueKind> const& queue, auto&& dest, auto const& source)
258 {
259 memcpy(queue, ALPAKA_FORWARD(dest), source, internal::getExtents(dest));
260 }
261
262 /** copy data byte wise from one to another container
263 *
264 * @param queue the copy will be executed after all previous work in this queue is finished
265 * @param[in,out] dest can be a container/view where the data should be written to
266 * @param[in] source can be a container/view from which the data will be copied
267 * @param extents M-dimensional data extents in elements, can be smaller than the container capacity
268 */
269 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
270 inline void memcpy(
271 Queue<T_Device, T_QueueKind> const& queue,
272 auto&& dest,
273 auto const& source,
274 alpaka::concepts::VectorOrScalar auto const& extents)
275 {
276 Vec const extentsVec = extents;
278 std::decay_t<decltype(*queue.get())>,
279 std::decay_t<decltype(dest)>,
280 std::decay_t<decltype(source)>,
281 std::decay_t<decltype(extentsVec)>>{}(*queue.get(), ALPAKA_FORWARD(dest), source, extentsVec);
282 }
283
284 /** copy data byte wise from a container or host pointer to global device memory
285 *
286 * @param queue the copy will be executed after all previous work in this queue is finished
287 * @param[in,out] dest must be device global memory on the device of the queue the data should be written to
288 * @param[in] source can be a container/view or host accessible pointer from which the data will be copied
289 */
290 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind, typename T_Storage, typename T>
291 inline void memcpy(
292 Queue<T_Device, T_QueueKind> const& queue,
294 auto&& source)
295 {
297 std::decay_t<decltype(*queue.get())>,
299 std::decay_t<decltype(source)>>{}(*queue.get(), dest, ALPAKA_FORWARD(source));
300 }
301
302 /** copy data byte wise from global device memory to a container or host pointer
303 *
304 * @param queue the copy will be executed after all previous work in this queue is finished
305 * @param[in,out] dest can be a container/view or host accessible pointer the data should be written to
306 * @param[in] source must be device global memory on the device of the queue from which the data will be copied
307 */
308 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind, typename T_Storage, typename T>
309 inline void memcpy(
310 Queue<T_Device, T_QueueKind> const& queue,
311 auto&& dest,
313 {
315 std::decay_t<decltype(*queue.get())>,
316 std::decay_t<decltype(dest)>,
318 }
319
320 /** fill memory byte wise
321 *
322 * @param[in,out] dest can be a container/view where the data should be written to
323 * The caller should ensure that the memory is valid until the operation is completed not until the
324 * execution handle is given back because alpaka is not extending the life-time until the operation
325 * is finished.
326 * @param byteValue value to be written to each byte
327 */
328 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
329 inline void memset(Queue<T_Device, T_QueueKind> const& queue, auto&& dest, uint8_t byteValue)
330 {
331 memset(queue, ALPAKA_FORWARD(dest), byteValue, internal::getExtents(dest));
332 }
333
334 /** fill memory byte wise
335 *
336 * @param[in,out] dest can be a container/view where the data should be written to
337 * The caller should ensure that the memory is valid until the operation is completed not until the
338 * execution handle is given back because alpaka is not extending the life-time until the operation
339 * is finished.
340 * @param byteValue value to be written to each byte
341 * @param extents M-dimensional data extents in elements, can be smaller than the container capacity
342 */
343 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
344 inline void memset(
345 Queue<T_Device, T_QueueKind> const& queue,
346 auto&& dest,
347 uint8_t byteValue,
348 alpaka::concepts::VectorOrScalar auto const& extents)
349 {
350 Vec const extentsVec = extents;
352 std::decay_t<decltype(*queue.get())>,
353 std::decay_t<decltype(dest)>,
354 std::decay_t<decltype(extentsVec)>>{}(*queue.get(), ALPAKA_FORWARD(dest), byteValue, extentsVec);
355 }
356
357 /** fill memory element wise
358 *
359 * @param[in,out] dest can be a container/view where the data should be written to
360 * The caller should ensure that the memory is valid until the operation is completed not until the
361 * execution handle is given back because alpaka is not extending the life-time until the operation
362 * is finished.
363 * @param elementValue value to be written to each element
364 */
365 template<typename T_Value, typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
366 inline void fill(Queue<T_Device, T_QueueKind> const& queue, auto&& dest, T_Value elementValue) requires(
367 std::same_as<alpaka::trait::GetValueType_t<ALPAKA_TYPEOF(dest)>, T_Value>
369 {
370 fill(queue, ALPAKA_FORWARD(dest), elementValue, internal::getExtents(dest));
371 }
372
373 /** fill memory element wise
374 *
375 * @param[in,out] dest can be a container/view where the data should be written to
376 * The caller should ensure that the memory is valid until the operation is completed not until the
377 * execution handle is given back because alpaka is not extending the life-time until the operation
378 * is finished.
379 * @param elementValue value to be written to each element
380 * @param extents M-dimensional data extents in elements, can be smaller than the container capacity
381 */
382
383 template<typename T_Value, typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
384 inline void fill(
385 Queue<T_Device, T_QueueKind> const& queue,
386 auto&& dest,
387 T_Value elementValue,
388 alpaka::concepts::VectorOrScalar auto const& extents)
389 requires(
390 std::same_as<alpaka::trait::GetValueType_t<ALPAKA_TYPEOF(dest)>, T_Value>
391 && std::
393 {
394 Vec const extentsVec = extents;
396 ALPAKA_TYPEOF(*queue.get()),
397 ALPAKA_TYPEOF(dest),
398 ALPAKA_TYPEOF(elementValue),
399 ALPAKA_TYPEOF(extentsVec)>{}(*queue.get(), ALPAKA_FORWARD(dest), elementValue, extentsVec);
400 }
401
402 /** @} */
403
404 /** @{
405 * @name Deferred device allocations
406 */
407 /** allocate memory that is accessible after it is processed in the queue
408 *
409 * Deferred allocation means that the pointer in the returned buffer is valid after the function is returning.
410 * It is allowed to slice the buffer or use the encapsulated pointer for address calculations.
411 * In any cases the pointer is not allowed to be dereferenced before the memory allocation is processed in the
412 * queue. All tasks performing any memory access must be executed after the memory allocation is processed in the
413 * queue. This can be achieved by waiting on the queue or describing queue dependencies via @c waitFor(). The
414 * memory is allowed to be used in other queues too. To avoid that a view to the memory is still in use during the
415 * deallocation you can use @see addDestructorAction() and wait for a queue if it **differs** to the queue used for
416 * the allocation.
417 * The first access could have higher latency compared to alpaka::onHost::alloc() due to the initial setup of the
418 * caching allocator used by some APIs. But subsequent accesses should have lower latency.
419 *
420 * @attention It is allowed that the function is blocking the caller until the memory is created.
421 *
422 * @tparam T_Type type of the data elements
423 * @param queue queue handle
424 * @param extents number of elements for each dimension
425 * @return Shared buffer to the allocated memory. The buffer will be freed after the last instance to the
426 * memory is destroyed. The deallocation is asynchronous performed in the queue which is used for the
427 * allocation.
428 */
429 template<typename T_Type, typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
430 inline auto allocDeferred(
431 Queue<T_Device, T_QueueKind> const& queue,
432 alpaka::concepts::VectorOrScalar auto const& extents)
433 {
434 Vec const extentsVec = extents;
435 return internal::AllocDeferred::Op<T_Type, std::decay_t<decltype(*queue.get())>, ALPAKA_TYPEOF(extentsVec)>{}(
436 *queue.get(),
437 extentsVec);
438 }
439
440 /** allocate memory that is accessible after it is processed in the queue
441 *
442 * In any cases the pointer is not allowed to be dereferenced before the memory allocation is processed in the
443 * queue. All tasks performing any memory access must be executed after the memory allocation is processed in the
444 * queue. This can be achieved by waiting on the queue or describing queue dependencies via @c waitFor(). The
445 * memory is allowed to be used in other queues too. To avoid that a view to the memory is still in use during the
446 * deallocation you can use @see addDestructorAction() and wait for a queue if it **differs** to the queue used for
447 * the allocation.
448 * The first access could have higher latency compared to alpaka::onHost::alloc() due to the initial setup of the
449 * caching allocator used by some APIs. But subsequent accesses should have lower latency.
450 *
451 * @attention It is allowed that the function is blocking the caller until the memory is created.
452 *
453 * @param queue queue handle
454 * @param[in] view other memory where the extents will be derived from
455 * @return Shared buffer to the allocated memory. The buffer will be freed after the last instance to the
456 * memory is destroyed. The deallocation is asynchronous performed in the queue which is used for the
457 * allocation.
458 */
459 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
460 inline auto allocLikeDeferred(Queue<T_Device, T_QueueKind> const& queue, auto const& view)
461 {
463 }
464
465 /** @} */
466} // namespace alpaka::onHost
The class used to bind kernel function object and arguments together. Once an instance of this class ...
#define ALPAKA_TYPEOF(...)
Get the type of instance.
Definition common.hpp:154
#define ALPAKA_FORWARD(instance)
Perfectly forward an instance as argument.
Definition common.hpp:148
Concept to check for APIs.
Definition api.hpp:42
Concept to check if something is a device kind.
Definition tag.hpp:145
Concept to check if a type is a KernelBundle.
Concept to check if a type is a queue kind.
Definition tag.hpp:74
Concept to check if a type is a vector or scalar variable.
Definition Vec.hpp:65
Concept to check if a type is a ThreadSpec or a FrameSpec.
constexpr AnyExecutor anyExecutor
Automatic executor selection.
Definition executor.hpp:33
alpaka'S function interface
Definition fn.hpp:38
constexpr auto getDeviceKind(auto &&any) -> decltype(GetDeviceType::Op< ALPAKA_TYPEOF(any)>{}(any))
constexpr auto getApi(auto &&any) -> decltype(GetApi::Op< ALPAKA_TYPEOF(any)>{}(any))
Definition interface.hpp:62
bool isQueueEmpty(auto &queue)
constexpr auto getDevice(auto &&any)
Definition interface.hpp:78
auto getNativeHandle(auto &&any)
Definition interface.hpp:95
void waitFor(auto &queue, auto &event)
auto getExtents(auto &&any)
void enqueue(auto &queue, onHost::concepts::ThreadOrFrameSpec auto const &launchCfg, KernelBundle< TKernelFn, TArgs... > const &kernelBundle)
Functionality which is usable on the host CPU controller thread.
Definition api.hpp:40
auto allocDeferred(Queue< T_Device, T_QueueKind > const &queue, alpaka::concepts::VectorOrScalar auto const &extents)
allocate memory that is accessible after it is processed in the queue
Definition Queue.hpp:430
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
std::shared_ptr< T > Handle
Definition Handle.hpp:30
auto allocLikeDeferred(Queue< T_Device, T_QueueKind > const &queue, auto const &view)
allocate memory that is accessible after it is processed in the queue
Definition Queue.hpp:460
decltype(auto) makeAccessibleOnAcc(auto &&any)
Provides an instance of an object which can be used within the compute kernel.
void memcpy(Queue< T_Device, T_QueueKind > const &queue, auto &&dest, auto const &source)
copy data byte wise from one to another container
Definition Queue.hpp:257
Queue(Handle< T_Queue > &&, T_QueueKind) -> Queue< Device< ALPAKA_TYPEOF(alpaka::internal::getApi(std::declval< T_Queue >())), ALPAKA_TYPEOF(alpaka::internal::getDeviceKind(std::declval< T_Queue >()))>, T_QueueKind >
constexpr bool isThreadSpec_v
constexpr bool isFrameSpec_v
void memset(Queue< T_Device, T_QueueKind > const &queue, auto &&dest, uint8_t byteValue)
fill memory byte wise
Definition Queue.hpp:329
typename GetValueType< T >::type GetValueType_t
Definition trait.hpp:65
constexpr decltype(auto) getExecutor(auto &&any)
Get the executor associated with an object.
Definition interface.hpp:23
constexpr decltype(auto) get(concepts::SpecializationOf< Dict > auto &t) noexcept
Definition Dict.hpp:156
STL namespace.
Helper class to provide access to device global memory variables.
Description of a specific device that one can schedule kernels on.
Definition Device.hpp:32
Device/Api-agnostic description of the logical parallelism exposed to a kernel.
Definition FrameSpec.hpp:46
auto getDevice() const
Get the device of this queue.
Definition Queue.hpp:98
void waitFor(Event< Device< T_Api, T_DeviceKind > > const &event) const
Wait until all operations in this queue are finished.
Definition Queue.hpp:216
ALPAKA_TYPEOF( internal::MakeQueue::Op< ALPAKA_TYPEOF(*std::declval< DeviceInterface >().get()), T_QueueKind >{}( *std::declval< DeviceInterface >().get(), T_QueueKind{})) QueueHandle
Definition Queue.hpp:34
void enqueue(Event< Device< T_Api, T_DeviceKind > > const &event) const
Enqueue an event.
Definition Queue.hpp:205
void enqueueHostFn(auto const &task) const
Enqueue an operation which is executed on the host side.
Definition Queue.hpp:159
constexpr alpaka::concepts::Api auto getApi() const
Definition Queue.hpp:54
void enqueueHostFnDeferred(auto const &task) const
Enqueue an operation which is executed asynchronously on the host side.
Definition Queue.hpp:172
bool isEmpty() const
Checks if the queue does not have any enqueued work to process.
Definition Queue.hpp:230
void enqueueNativeFn(auto const &fn) const
Enqueue a native Api call.
Definition Queue.hpp:194
void enqueue(onHost::concepts::ThreadOrFrameSpec auto const &launchCfg, auto const &f, auto &&... args) const
Enqueue a kernel functor to a queue.
Definition Queue.hpp:146
void enqueue(onHost::concepts::ThreadOrFrameSpec auto const &launchCfg, alpaka::concepts::KernelBundle auto const &kernelBundle) const
Enqueue a kernel functor to a queue.
Definition Queue.hpp:109
constexpr alpaka::concepts::QueueKind auto getQueueKind() const
Definition Queue.hpp:59
constexpr alpaka::concepts::DeviceKind auto getDeviceKind() const
Definition Queue.hpp:64
Backend-specific description of the actual block and thread launch shape.