alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
interface.hpp
Go to the documentation of this file.
1/* Copyright 2024 René Widera, Tim Hanel
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
15#include "alpaka/tag.hpp"
16
17namespace alpaka::onAcc::internal
18{
19 // forward declaration to avoid cyclic includes
20 template<typename T_Storage, typename T_Type>
21 struct GlobalDeviceMemoryWrapper;
22} // namespace alpaka::onAcc::internal
23
24namespace alpaka::onHost
25{
26 namespace internal
27 {
28 struct MakePlatform
29 {
30 template<typename T_Api, alpaka::concepts::DeviceKind T_DeviceKind>
31 struct Op
32 {
33 auto operator()(T_Api api, T_DeviceKind deviceType) const;
34 };
35 };
36
37 static auto makePlatform(auto api, alpaka::concepts::DeviceKind auto deviceType)
38 {
39 return MakePlatform::Op<ALPAKA_TYPEOF(api), ALPAKA_TYPEOF(deviceType)>{}(api, deviceType);
40 }
41
42 struct GetDeviceCount
43 {
44 template<typename T_Platform>
45 struct Op
46 {
47 uint32_t operator()(T_Platform& platform) const
48 {
49 return platform.getDeviceCount();
50 }
51 };
52 };
53
54 struct MakeDevice
55 {
56 template<typename T_Platform>
57 struct Op
58 {
59 auto operator()(auto& platform, uint32_t idx) const
60 {
61 return platform.makeDevice(idx);
62 }
63 };
64 };
65
66 struct GetDevice
67 {
68 template<typename T_Any>
69 struct Op
70 {
71 auto operator()(T_Any const& any) const
72 {
73 return any.getDevice();
74 }
75 };
76 };
77
78 inline constexpr auto getDevice(auto&& any)
79 {
80 return GetDevice::Op<ALPAKA_TYPEOF(any)>{}(any);
81 }
82
83 struct GetNativeHandle
84 {
85 template<typename T_Any>
86 struct Op
87 {
88 auto operator()(T_Any const& any) const
89 {
90 return any.getNativeHandle();
91 }
92 };
93 };
94
95 inline auto getNativeHandle(auto&& any)
96 {
97 return GetNativeHandle::Op<ALPAKA_TYPEOF(any)>{}(any);
98 }
99
100 struct MakeQueue
101 {
102 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
103 struct Op
104 {
105 auto operator()(T_Device& device, T_QueueKind) const
106 {
107 return device.makeQueue(T_QueueKind{});
108 }
109 };
110 };
111
112 struct MakeEvent
113 {
114 template<typename T_Device>
115 struct Op
116 {
117 auto operator()(T_Device& device) const
118 {
119 return device.makeEvent();
120 }
121 };
122 };
123
124 struct Wait
125 {
126 template<typename T_Any>
127 struct Op
128 {
129 void operator()(T_Any& any)
130 {
131 any.wait();
132 }
133 };
134 };
135
136 inline void wait(auto&& any)
137 {
138 Wait::Op<ALPAKA_TYPEOF(any)>{}(any);
139 }
140
141 struct WaitFor
142 {
143 template<typename T_Queue, typename T_Event>
144 struct Op
145 {
146 void operator()(T_Queue& queue, T_Event& event)
147 {
148 queue.waitFor(event);
149 }
150 };
151 };
152
153 inline void waitFor(auto& queue, auto& event)
154 {
155 WaitFor::Op<ALPAKA_TYPEOF(queue), ALPAKA_TYPEOF(event)>{}(queue, event);
156 }
157
158 struct IsEventComplete
159 {
160 template<typename T_Any>
161 struct Op
162 {
163 bool operator()(T_Any& any)
164 {
165 return any.isEventComplete();
166 }
167 };
168 };
169
170 inline bool isEventComplete(auto&& any)
171 {
172 return IsEventComplete::Op<ALPAKA_TYPEOF(any)>{}(any);
173 }
174
175 struct IsQueueEmpty
176 {
177 template<typename T_Queue>
178 struct Op
179 {
180 bool operator()(T_Queue& queue)
181 {
182 return queue.isQueueEmpty();
183 }
184 };
185 };
186
187 inline bool isQueueEmpty(auto& queue)
188 {
189 return IsQueueEmpty::Op<ALPAKA_TYPEOF(queue)>{}(queue);
190 }
191
192 struct Enqueue
193 {
194 template<
195 typename T_Queue,
196 onHost::concepts::ThreadOrFrameSpec T_LaunchCfg,
197 alpaka::concepts::KernelBundle T_KernelBundle>
198 struct Kernel
199 {
200 void operator()(T_Queue& queue, T_LaunchCfg const& launchCfg, T_KernelBundle const& kernelBundle) const
201 {
202 queue.enqueue(launchCfg, kernelBundle);
203 }
204 };
205
206 template<typename T_Queue, typename T_Task>
207 struct HostTask
208 {
209 void operator()(T_Queue& queue, T_Task const& task) const
210 {
211 queue.enqueueHostFn(task);
212 }
213 };
214
215 template<typename T_Queue, typename T_Task>
216 struct HostTaskDeferred
217 {
218 void operator()(T_Queue& queue, T_Task const& task) const
219 {
220 queue.enqueueHostFnDeferred(task);
221 }
222 };
223
224 template<typename T_Queue, typename T_Task>
225 struct NativeFn
226 {
227 void operator()(T_Queue& queue, T_Task const& fn) const
228 {
229 queue.enqueueNativeFn(fn);
230 }
231 };
232
233 template<typename T_Queue, typename T_Event>
234 struct Event
235 {
236 void operator()(T_Queue& queue, T_Event& event) const
237 {
238 queue.enqueue(event);
239 }
240 };
241 };
242
243 inline void enqueueHostFn(auto& queue, auto const& task)
244 {
245 Enqueue::HostTask<ALPAKA_TYPEOF(queue), ALPAKA_TYPEOF(task)>{}(queue, task);
246 }
247
248 inline void enqueueHostFnDeferred(auto& queue, auto const& task)
249 {
250 Enqueue::HostTaskDeferred<ALPAKA_TYPEOF(queue), ALPAKA_TYPEOF(task)>{}(queue, task);
251 }
252
253 template<typename TKernelFn, typename... TArgs>
254 inline void enqueue(
255 auto& queue,
256 onHost::concepts::ThreadOrFrameSpec auto const& launchCfg,
257 KernelBundle<TKernelFn, TArgs...> const& kernelBundle)
258 {
259 Enqueue::Kernel<ALPAKA_TYPEOF(queue), ALPAKA_TYPEOF(launchCfg), KernelBundle<TKernelFn, TArgs...>>{}(
260 queue,
261 launchCfg,
262 kernelBundle);
263 }
264
265 struct AdjustThreadSpec
266 {
267 template<
268 typename T_Device,
269 onHost::concepts::FrameSpec T_FrameSpec,
270 alpaka::concepts::KernelBundle T_KernelBundle>
271 struct Op
272 {
273 auto operator()(
274 T_Device const& device,
275 T_FrameSpec const& frameSpec,
276 T_KernelBundle const& kernelBundle) const
277 {
278 alpaka::unused(device, frameSpec.getExecutor(), kernelBundle);
279 return ThreadSpec{frameSpec.getNumFrames(), frameSpec.getFrameExtents(), frameSpec.getExecutor()};
280 }
281 };
282 };
283
284 template<typename TKernelFn, typename... TArgs>
285 static auto adjustThreadSpec(
286 auto const& device,
287 onHost::concepts::FrameSpec auto const& frameSpec,
288 KernelBundle<TKernelFn, TArgs...> const& kernelBundle)
289 {
290 return AdjustThreadSpec::
291 Op<ALPAKA_TYPEOF(device), ALPAKA_TYPEOF(frameSpec), KernelBundle<TKernelFn, TArgs...>>{}(
292 device,
293 frameSpec,
294 kernelBundle);
295 }
296
297 struct Data
298 {
299 template<typename T_Any>
300 struct Op
301 {
302 decltype(auto) operator()(auto&& any) const
303 {
304 return std::data(any);
305 }
306 };
307
308 static decltype(auto) data(auto&& any)
309 {
310 return Op<ALPAKA_TYPEOF(any)>{}(any);
311 }
312
313 template<typename T_Any>
314 static decltype(auto) data(Handle<T_Any>&& anyHandle)
315 {
316 return Op<ALPAKA_TYPEOF(*anyHandle.get())>{}(*anyHandle.get());
317 }
318 };
319
320 struct Alloc
321 {
322 template<typename T_Type, typename T_Any, typename T_Extents>
323 struct Op
324 {
325 void operator()(T_Any& any, T_Extents const&) const;
326 };
327 };
328
329 struct AllocDeferred
330 {
331 template<typename T_Type, typename T_Any, typename T_Extents>
332 struct Op
333 {
334 void operator()(T_Any& any, T_Extents const&) const;
335 };
336 };
337
338 struct AllocUnified
339 {
340 template<typename T_Type, typename T_Any, typename T_Extents>
341 struct Op
342 {
343 void operator()(T_Any& any, T_Extents const&) const;
344 };
345 };
346
347 struct AllocMapped
348 {
349 template<typename T_Type, typename T_Any, typename T_Extents>
350 struct Op
351 {
352 void operator()(T_Any& any, T_Extents const&) const;
353 };
354 };
355
356 /** checks if a view can be accessed from the given device
357 *
358 * There are two paths to check if a view is accessible:
359 * - first: Try to validate the view in the scope of the device.
360 * - second: Try to validate based on soft criteria in the scope of the view's API.
361 * This path is required because the host API does not know about view data locations.
362 * The second path is optionally and will return always false if not specialized.
363 */
364 struct IsDataAccessible
365 {
366 template<typename T_Device, typename T_Any>
367 struct FirstPath
368 {
369 bool operator()(T_Device& device, T_Any const& any) const;
370 };
371
372 template<typename T_DataApi, alpaka::concepts::DeviceKind T_DeviceKind, typename T_Any>
373 struct SecondPath
374 {
375 bool operator()(T_DataApi, T_DeviceKind, T_Any const&) const
376 {
377 return false;
378 }
379 };
380 };
381
382 struct Memcpy
383 {
384 template<typename T_Queue, typename T_Dest, typename T_Source, typename T_Extents>
385 struct Op
386 {
387 void operator()(T_Queue& queue, auto&&, T_Source const&, T_Extents const&) const;
388 };
389 };
390
391 struct MemcpyDeviceGlobal
392 {
393 template<typename T_Queue, typename T_Dest, typename T_Source>
394 struct Op
395 {
396 /** copy data from or to the device global memory
397 *
398 * It is only allowed to copy data from or to the host.
399 * Copy from device global variable to device global variables is not supported.
400 * The host data is allowed te be a host accessible pointer.
401 */
402 void operator()(T_Queue& queue, T_Dest&&, T_Source&&) const;
403 };
404 };
405
406 struct Memset
407 {
408 template<typename T_Queue, typename T_Dest, typename T_Extents>
409 struct Op
410 {
411 void operator()(T_Queue& queue, auto&&, uint8_t, T_Extents const&) const;
412 };
413 };
414
415 struct Fill
416 {
417 template<typename T_Queue, typename T_Dest, typename T_Value, typename T_Extents>
418 struct Op
419 {
420 void operator()(T_Queue& queue, auto&&, T_Value, T_Extents const&) const;
421 };
422 };
423
424 struct GetDeviceProperties
425 {
426 template<typename T_Any>
427 struct Op
428 {
429 DeviceProperties operator()(auto const& platform, uint32_t idx) const;
430
431 DeviceProperties operator()(auto const& device) const;
432 };
433 };
434
435 struct GetFreeGlobalMemBytes
436 {
437 template<typename T_Any>
438 struct Op
439 {
440 size_t operator()(auto const& device) const
441 {
442 return device.getFreeGlobalMemBytes();
443 }
444 };
445 };
446
447 inline DeviceProperties getDeviceProperties(auto const& platform, uint32_t idx)
448 {
449 return GetDeviceProperties::Op<ALPAKA_TYPEOF(platform)>{}(platform, idx);
450 }
451
452 struct GetExtents
453 {
454 template<typename T_Any>
455 struct Op
456 {
457 decltype(auto) operator()(auto&& any) const
458 {
459 return any.getExtents();
460 }
461 };
462 };
463
464 inline auto getExtents(auto&& any)
465 {
466 return GetExtents::Op<ALPAKA_TYPEOF(any)>{}(any);
467 }
468
469 template<typename T_Any>
470 inline auto getExtents(Handle<T_Any>&& any)
471 {
472 return GetExtents::Op<ALPAKA_TYPEOF(*any.get())>{}(*any.get());
473 }
474
475 struct GetPitches
476 {
477 template<typename T_Any>
478 struct Op
479 {
480 decltype(auto) operator()(auto&& any) const
481 {
482 return any.getPitches();
483 }
484 };
485 };
486
487 inline auto getPitches(auto&& any)
488 {
489 return GetPitches::Op<ALPAKA_TYPEOF(any)>{}(any);
490 }
491
492 template<typename T_Any>
493 inline auto getPitches(Handle<T_Any>&& any)
494 {
495 return GetPitches::Op<ALPAKA_TYPEOF(*any.get())>{}(*any.get());
496 }
497
498 /** Provide a frame specification for the given extents
499 *
500 * @param internalDevice must be an alpaka internal device implementation
501 */
502 inline constexpr auto getFrameSpec(
503 auto const& internalDevice,
504 alpaka::concepts::Executor auto executor,
505 alpaka::concepts::VectorOrScalar auto const& extents)
506 {
507 static_assert(executor != exec::anyExecutor, "'exec::anyExecutor' can not be used here");
508 Vec extentMd = extents;
509 using ExtentVecType = ALPAKA_TYPEOF(extentMd);
510 // check that all extent dimensions are greater than zero
511 ALPAKA_ASSERT((extentMd > ExtentVecType::fill(0u)).reduce(std::logical_and{}));
513 auto props = internal::GetDeviceProperties::Op<ALPAKA_TYPEOF(internalDevice)>{}(internalDevice);
514 IndexType warpSize = static_cast<IndexType>(props.warpSize);
515 // try to create a specification with a frame size of 512 elements
516 IndexType numFrameElements = 512;
517 // avoid non-power of two values
518 IndexType fastDimensionValue = roundDownToPowerOfTwo(std::min(warpSize, extentMd.x()));
519 ExtentVecType frameExtents = ExtentVecType::fill(1).rAssign(fastDimensionValue);
520 numFrameElements /= frameExtents.x();
521 // distribute remainder frame elements
522 while(numFrameElements > IndexType{1})
523 {
524 uint32_t maxIdx = ExtentVecType::dim() - 1u;
525 IndexType maxValue = 0;
526 for(auto i = 0u; i < ExtentVecType::dim(); ++i)
527 {
528 auto v = extentMd[i] / frameExtents[i] / IndexType{2};
529 if(maxValue < v)
530 {
531 maxIdx = i;
532 maxValue = v;
533 }
534 }
535 // apply the change only if we not oversubscribe the extents
536 auto v = extentMd[maxIdx] / frameExtents[maxIdx] / IndexType{2};
537 if(v >= IndexType{1})
538 frameExtents[maxIdx] *= IndexType{2};
539 else
540 break;
541 numFrameElements /= IndexType{2};
542 }
543
544 ExtentVecType numFrames = divExZero(extentMd, frameExtents);
545 auto frameSpec = FrameSpec{numFrames, frameExtents, executor};
546 return frameSpec;
547 }
548
549 /** Provides a SIMD optimized frame specification
550 *
551 * The frame specification is optimized for a flat non-hierarchical execution via onAcc::worker::threadsInGrid.
552 *
553 * @tparam T_DataType the data type for which you would like to SIMD optimize
554 * @param internalDevice must be a alpaka internal device implementation
555 */
556 template<typename T_DataType>
557 inline constexpr auto getSimdFrameSpec(
558 auto const& internalDevice,
559 alpaka::concepts::Executor auto executor,
560 alpaka::concepts::VectorOrScalar auto const& extents)
561 {
562 static_assert(executor != exec::anyExecutor, "'exec::anyExecutor' can not be used here");
563 Vec extentMd = extents;
564 auto deviceKind = alpaka::internal::getDeviceKind(internalDevice);
565 auto deviceApi = alpaka::internal::getApi(internalDevice);
566 using ExtentVecType = ALPAKA_TYPEOF(extentMd);
567 // check that all extent dimensions are greater than zero
568 ALPAKA_ASSERT((extentMd > ExtentVecType::fill(0u)).reduce(std::logical_and{}));
570
571 ExtentVecType frameExtents = getFrameSpec(internalDevice, executor, extents).getFrameExtents();
572
573 IndexType elementsPerFrameItem
574 = static_cast<IndexType>(getNumElemPerThread<T_DataType>(deviceApi, deviceKind));
575
576 /* The number of frames depends on an imaginary frame extent where each frame item is computing multiple
577 * elements from the problem extents.
578 */
579 ExtentVecType numFrames
580 = divExZero(extentMd, frameExtents * frameExtents.fill(1).rAssign(elementsPerFrameItem));
581 // The frame specification is not required to be a multiple of the extent, it can be smaller.
582 FrameSpec frameSpec = FrameSpec{numFrames, frameExtents, executor};
583 return frameSpec;
584 }
585 } // namespace internal
586} // namespace alpaka::onHost
#define ALPAKA_ASSERT(...)
The assert can be explicit disabled by defining NDEBUG.
Definition Assert.hpp:14
#define ALPAKA_TYPEOF(...)
Get the type of instance.
Definition common.hpp:154
constexpr AnyExecutor anyExecutor
Automatic executor selection.
Definition executor.hpp:33
constexpr WarpSize warpSize
Definition tag.hpp:42
constexpr DeviceKind deviceKind
Definition tag.hpp:30
constexpr Api api
Definition tag.hpp:24
constexpr Device device
Definition scope.hpp:70
constexpr bool any(alpaka::onAcc::concepts::Acc auto const &acc, int32_t predicate)
Evaluates predicate for all active threads of the warp.
Definition warp.hpp:83
constexpr auto queue
Definition lvl.hpp:127
constexpr auto event
Definition lvl.hpp:97
Functionality which is usable on the host CPU controller thread.
Definition api.hpp:40
FrameSpec(T_NumFrames const &, T_FrameExtents const &) -> FrameSpec< alpaka::trait::getVec_t< T_NumFrames >, alpaka::trait::getVec_t< T_FrameExtents >, alpaka::exec::AnyExecutor >
std::shared_ptr< T > Handle
Definition Handle.hpp:30
ThreadSpec(T_NumBlocks const &, T_NumThreads const &) -> ThreadSpec< alpaka::trait::getVec_t< T_NumBlocks >, alpaka::trait::getVec_t< T_NumThreads > >
void reduce(Queue< T_Device, T_QueueKind > const &queue, alpaka::concepts::Executor auto const exec, DataType const &neutralElement, alpaka::concepts::IMdSpan auto out, auto &&binaryReduceFn, auto &&in)
accumulate the results into a scalar value.
Definition reduce.hpp:29
typename GetValueType< T >::type GetValueType_t
Definition trait.hpp:65
ALPAKA_FN_HOST_ACC constexpr auto divExZero(Integral a, Integral b) -> Integral
Returns the max(a / b, 1) as integer.
Definition utility.hpp:41
consteval uint32_t getNumElemPerThread(concepts::Api auto const api, alpaka::concepts::DeviceKind auto const deviceType)
Get the number of elements to compute per thread.
Definition trait.hpp:177
constexpr T roundDownToPowerOfTwo(T value)
round to the next power of two which is equal or lower to the value
Definition utility.hpp:88
ALPAKA_FN_HOST_ACC Vec(T_1, T_Args...) -> Vec< T_1, uint32_t(sizeof...(T_Args)+1u), ArrayStorage< T_1, uint32_t(sizeof...(T_Args)+1u)> >
ALPAKA_FN_HOST KernelBundle(TKernelFn const &, TArgs &&...) -> KernelBundle< TKernelFn, TArgs... >
User defined deduction guide with trailing return type. For CTAD during the construction.