alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
Device.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"
13#include "alpaka/tag.hpp"
14#include "alpaka/utility.hpp"
15
16#include <bit>
17#include <climits>
18
19namespace alpaka::onHost
20{
21 /** @brief Description of a specific device that one can schedule kernels on.
22 *
23 * @details
24 * A device is the combination of an alpaka::deviceKind::onHost::DeviceKind and an alpaka::concepts::Api,
25 * representing an entity that one can schedule work on.
26 *
27 * @tparam T_Api The Api powering this device.
28 * @tparam T_DeviceKind The kind of device it is.
29 */
30 template<alpaka::concepts::Api T_Api, alpaka::concepts::DeviceKind T_DeviceKind>
31 struct Device
32 {
33 private:
34 using PlatformHandle = ALPAKA_TYPEOF(internal::makePlatform(T_Api{}, T_DeviceKind{}));
35 using DeviceHandle = ALPAKA_TYPEOF(
36 internal::MakeDevice::Op<typename PlatformHandle::element_type>{}(
37 *std::declval<PlatformHandle>().get(),
38 0u));
39 DeviceHandle m_device;
40
41 public:
42 friend struct alpaka::internal::GetName;
43 friend struct internal::GetNativeHandle;
44
45 using element_type = typename DeviceHandle::element_type;
46
47 auto get() const
48 {
49 return m_device.get();
50 }
51
52 template<typename T_Device>
53 Device(Handle<T_Device>&& internalDeviceHandle)
54 : m_device{std::forward<Handle<T_Device>>(internalDeviceHandle)}
55 {
56 }
57
58 void _()
59 {
60 static_assert(internal::concepts::Device<element_type>);
61 }
62
63 std::string getName() const
64 {
65 return alpaka::internal::GetName::Op<std::decay_t<decltype(*m_device.get())>>{}(*m_device.get());
66 }
67
68 [[nodiscard]] auto getNativeHandle() const
69 {
70 return internal::getNativeHandle(*m_device.get());
71 }
72
73 bool operator==(Device const& other) const
74 {
75 return this->get() == other.get();
76 }
77
78 bool operator!=(Device const& other) const
79 {
80 return this->get() != other.get();
81 }
82
83 /** Create a queue for this device.
84 *
85 * @attention If you call this method multiple times it is allowed that you always get the same handle
86 * back. There is no guarantee that you will get independent queues.
87 *
88 * Enqueuing tasks into two different queues does not guarantee that these tasks run in parallel.
89 * Running tasks from different tasks sequentially is valid behavior. Enqueuing into two individual queues only
90 * signifies that the tasks are independent of each other and their order of execution is independent.
91 *
92 * @param kind
93 * Blocking behaviour:
94 * - queueKind::nonBlocking (default): enqueue returns immediately; completion of the enqueued operation
95 * must be ensured via onHost::wait(queue) or by enqueuing dependent operations onto the same queue.
96 * - queueKind::blocking: each enqueue only returns after the operation is complete and its effects are
97 * host-visible.
98 *
99 * @return A onHost::Queue that tasks and memory operations can be enqueued on.
100 */
102 {
103 return Queue{
104 internal::MakeQueue::Op<ALPAKA_TYPEOF(*m_device.get()), ALPAKA_TYPEOF(kind)>{}(*m_device.get(), kind),
105 kind};
106 }
107
109 {
111 }
112
114 {
115 return Event{internal::MakeEvent::Op<std::decay_t<decltype(*m_device.get())>>{}(*m_device.get())};
116 }
117
118 /** Blocks the caller until the given handle executes all work
119 */
120 void wait()
121 {
122 return internal::wait(*m_device.get());
123 }
124
125 /** Properties of a given device
126 *
127 * @attention Currently only a handful of entries is available. The object will be refactored soon and will
128 * become most likely a compile time dictionary tu support optional entries.
129 */
130
132 {
133 return internal::GetDeviceProperties::Op<ALPAKA_TYPEOF(*m_device.get())>{}(*m_device.get());
134 }
135
137 {
138 return internal::GetFreeGlobalMemBytes::Op<ALPAKA_TYPEOF(*m_device.get())>{}(*m_device.get());
139 }
140
141 constexpr auto getDeviceKind() const
142 {
143 return T_DeviceKind{};
144 }
145
146 constexpr alpaka::concepts::Api auto getApi() const
147 {
148 return T_Api{};
149 }
150 };
151
152 namespace concepts
153 {
154 /** @brief Concept to check if something is a device.
155 *
156 * @details
157 * This concept checks for specializations of alpaka::onHost::Device. For more information on devices in
158 * alpaka, refer to the class documentation.
159 */
160 template<typename T_Device>
162 } // namespace concepts
163
164 template<typename T_Device>
166 ALPAKA_TYPEOF(alpaka::internal::getApi(std::declval<T_Device>())),
167 ALPAKA_TYPEOF(alpaka::internal::getDeviceKind(std::declval<T_Device>()))>;
168
169 /** @{
170 * @name Device allocations
171 */
172 /** Allocate memory on the given device
173 *
174 * @tparam T_Type type of the data elements
175 * @param device device handle
176 * @param extents number of elements for each dimension
177 * @return memory owning view to the allocated memory
178 */
179 template<typename T_Type>
180 inline auto alloc(concepts::Device auto const& device, alpaka::concepts::VectorOrScalar auto const& extents)
181 {
182 Vec const extentsVec = extents;
183 return internal::Alloc::Op<T_Type, std::decay_t<decltype(*device.get())>, ALPAKA_TYPEOF(extentsVec)>{}(
184 *device.get(),
185 extentsVec);
186 }
187
188 /** Allocate memory on the given device with unified virtual memory
189 *
190 * This memory can be accessed from all devices with the same Api and device kind. Depending on the backend e.g.
191 * OneApi memory can be accessed by other device kind devices if they are using the same native context. It is not
192 * allowed to access the data on two devices at the same time, this must be avoided by explicit synchronizations.
193 * Unified memory follows the rules of UVM memory of the device backend e.g. CUDA, HIP, ...
194 *
195 * @tparam T_Type type of the data elements
196 * @param device device handle
197 * @param extents number of elements for each dimension
198 * @return Managed view to the allocated memory
199 */
200 template<typename T_Type>
201 inline auto allocUnified(concepts::Device auto const& device, alpaka::concepts::VectorOrScalar auto const& extents)
202 {
203 Vec const extentsVec = extents;
204 return internal::AllocUnified::Op<T_Type, std::decay_t<decltype(*device.get())>, ALPAKA_TYPEOF(extentsVec)>{}(
205 *device.get(),
206 extentsVec);
207 }
208
209 /** Allocates unified memory on the device associated with the given queue.
210 *
211 * This memory can be accessed from all devices with the same Api and device kind. Depending on the backend e.g.
212 * OneApi memory can be accessed by other device kind devices if they are using the same native context. It is not
213 * allowed to access the data on two devices at the same time, this must be avoided by explicit synchronizations.
214 * Unified memory follows the rules of UVM memory of the device backend e.g. CUDA, HIP, ...
215 *
216 * @ingroup foo
217 *
218 * @tparam T_Type type of the data elements
219 * @param queue queue handle
220 * @param extents number of elements for each dimension
221 */
222 template<typename T_Type, typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
223 inline auto allocUnified(
224 Queue<T_Device, T_QueueKind> const& queue,
225 alpaka::concepts::VectorOrScalar auto const& extents)
226 {
227 Vec const extentsVec = extents;
228 return internal::AllocUnified::
229 Op<T_Type, std::decay_t<decltype(*queue.getDevice().get())>, ALPAKA_TYPEOF(extentsVec)>{}(
230 *queue.getDevice().get(),
231 extentsVec);
232 }
233
234 /** Allocate pinned memory on the host which is mapped into the address space of the device
235 *
236 * Mapped memory is located on the host and is transferred for each access via the PCIe/Nvlink bus. The performance
237 * on the device is mostly pure. Mapped memory should be used for host memory if you transfer memory between host
238 * and device via `onHost::memcpy()` because the transfer will be optimized for latency and performance.
239 *
240 * @tparam T_Type type of the data elements
241 * @param device device handle
242 * @param extents number of elements for each dimension
243 */
244 template<typename T_Type>
245 inline auto allocMapped(concepts::Device auto const& device, alpaka::concepts::VectorOrScalar auto const& extents)
246 {
247 Vec const extentsVec = extents;
248 return internal::AllocMapped::Op<T_Type, std::decay_t<decltype(*device.get())>, ALPAKA_TYPEOF(extentsVec)>{}(
249 *device.get(),
250 extentsVec);
251 }
252
253 /** Allocate pinned memory on the host which is mapped into the address space of the device
254 *
255 * Mapped memory is located on the host and is transferred for each access via the PCIe/Nvlink bus. The performance
256 * on the device is mostly pure. Mapped memory should be used for host memory if you transfer memory between host
257 * and device via `onHost::memcpy()` because the transfer will be optimized for latency and performance.
258 *
259 * @tparam T_Type type of the data elements
260 * @param queue queue handle
261 * @param extents number of elements for each dimension
262 */
263 template<typename T_Type, typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
264 inline auto allocMapped(
265 Queue<T_Device, T_QueueKind> const& queue,
266 alpaka::concepts::VectorOrScalar auto const& extents)
267 {
268 return allocMapped<T_Type>(queue.getDevice(), extents);
269 }
270
271 /** Allocate memory on the given device based on a view
272 *
273 * Derives type and extents of the memory from the view.
274 * The content of the memory is NOT copied to the created allocated memory.
275 *
276 * @param device device handle
277 * @param[in] view memory where properties will be derived from
278 *
279 * @return memory owning view to the allocated memory
280 */
281 inline auto allocLike(concepts::Device auto const& device, auto const& view)
282 {
283 return alloc<alpaka::trait::GetValueType_t<ALPAKA_TYPEOF(view)>>(device, internal::getExtents(view));
284 }
285
286 ///@}
287
288 /** Check if the given view is accessible on the given device
289 *
290 * @param device device handle
291 * @param view memory where properties will be derived from
292 * @return true if the view is accessible on the device, false otherwise.
293 * alpaka can not detect all memory access types therefore the result can be false even if the memory is accessible
294 * because the view was allocated with a UVM allocator.
295 *
296 */
297 inline bool isDataAccessible(concepts::Device auto const& device, alpaka::concepts::IView auto const& view)
298 {
299 return internal::IsDataAccessible::FirstPath<ALPAKA_TYPEOF(*device.get()), ALPAKA_TYPEOF(view)>{}(
300 *device.get(),
301 view)
302 || internal::IsDataAccessible::SecondPath<
303 ALPAKA_TYPEOF(getApi(view)),
305 ALPAKA_TYPEOF(view)>{}(getApi(view), getDeviceKind(device), view);
306 }
307
308 /** Check if the given view is accessible on the device of the given queue
309 *
310 * @param queue queue handle
311 */
312 template<typename T_Device, alpaka::concepts::QueueKind T_QueueKind>
314 {
315 return internal::IsDataAccessible::FirstPath<ALPAKA_TYPEOF(*queue.getDevice().get()), ALPAKA_TYPEOF(view)>{}(
316 *queue.getDevice().get(),
317 view)
318 || internal::IsDataAccessible::SecondPath<
319 ALPAKA_TYPEOF(getApi(view)),
320 ALPAKA_TYPEOF(getDeviceKind(queue.getDevice())),
321 ALPAKA_TYPEOF(view)>{}(getApi(view), getDeviceKind(queue.getDevice()), view);
322 }
323
324 /** Provides a frame specification to operate on a given index range
325 *
326 * @param extents size of the index range
327 * @return frame specification
328 */
329 template<typename T_Api, alpaka::concepts::DeviceKind T_DeviceKind>
330 inline constexpr concepts::FrameSpec auto getFrameSpec(
331 Device<T_Api, T_DeviceKind> const& device,
332 alpaka::concepts::Executor auto executor,
333 alpaka::concepts::VectorOrScalar auto const& extents)
334 {
335 if constexpr(executor == exec::anyExecutor)
336 {
337 auto usedExecutor = defaultExecutor(device);
338 return internal::getFrameSpec(*device.get(), usedExecutor, extents);
339 }
340 else
341 return internal::getFrameSpec(*device.get(), executor, extents);
342 }
343
344 /** Provides a frame specification to operate on a given index range
345 *
346 * The frame specification will be optimized for SIMD executions in the highest dimension
347 * for a flat non-hierarchical execution via onAcc::worker::threadsInGrid.
348 * Do not use this functions for kernel using hierarchical thread parallelism, in many cases the frame
349 * specification depends on the outer parallelism in the kernel.
350 *
351 * @tparam T_DataType the data type for which you would like to SIMD optimize
352 * @param extents number of elements for each dimension of the type T_DataType
353 * @return frame specification
354 */
355 template<typename T_DataType, typename T_Api, alpaka::concepts::DeviceKind T_DeviceKind>
357 Device<T_Api, T_DeviceKind> const& device,
358 alpaka::concepts::Executor auto executor,
359 alpaka::concepts::VectorOrScalar auto const& extents)
360 {
361 if constexpr(executor == exec::anyExecutor)
362 {
363 auto usedExecutor = defaultExecutor(device);
364 return internal::getSimdFrameSpec<T_DataType>(*device.get(), usedExecutor, extents);
365 }
366 else
367 return internal::getSimdFrameSpec<T_DataType>(*device.get(), executor, extents);
368 }
369} // namespace alpaka::onHost
#define ALPAKA_TYPEOF(...)
Get the type of instance.
Definition common.hpp:154
Concept to check for APIs.
Definition api.hpp:42
Concept to check for an executor.
Definition trait.hpp:133
Interface concept for objects describing api-related multidimensional memory access.
Definition IView.hpp:56
Concept to check if a type is a queue kind.
Definition tag.hpp:74
Validates if T is a specialization of the unspecialized template type U.
Definition utility.hpp:118
Concept to check if a type is a vector or scalar variable.
Definition Vec.hpp:65
Concept to check if something is a device.
Definition Device.hpp:161
Concept to check if a type is a FrameSpec.
constexpr AnyExecutor anyExecutor
Automatic executor selection.
Definition executor.hpp:33
Functionality which is usable on the host CPU controller thread.
Definition api.hpp:40
bool isDataAccessible(concepts::Device auto const &device, alpaka::concepts::IView auto const &view)
Check if the given view is accessible on the given device.
Definition Device.hpp:297
constexpr auto defaultExecutor(internal::concepts::DeviceHandle auto deviceHandle)
Select a default executor for the given device.
Definition trait.hpp:169
constexpr concepts::FrameSpec auto getSimdFrameSpec(Device< T_Api, T_DeviceKind > const &device, alpaka::concepts::Executor auto executor, alpaka::concepts::VectorOrScalar auto const &extents)
Provides a frame specification to operate on a given index range.
Definition Device.hpp:356
std::shared_ptr< T > Handle
Definition Handle.hpp:30
auto allocUnified(concepts::Device auto const &device, alpaka::concepts::VectorOrScalar auto const &extents)
Allocate memory on the given device with unified virtual memory.
Definition Device.hpp:201
constexpr concepts::FrameSpec auto getFrameSpec(Device< T_Api, T_DeviceKind > const &device, alpaka::concepts::Executor auto executor, alpaka::concepts::VectorOrScalar auto const &extents)
Provides a frame specification to operate on a given index range.
Definition Device.hpp:330
auto allocLike(concepts::Device auto const &device, auto const &view)
Allocate memory on the given device based on a view.
Definition Device.hpp:281
Device(Handle< T_Device > &&) -> Device< ALPAKA_TYPEOF(alpaka::internal::getApi(std::declval< T_Device >())), ALPAKA_TYPEOF(alpaka::internal::getDeviceKind(std::declval< T_Device >()))>
auto allocMapped(concepts::Device auto const &device, alpaka::concepts::VectorOrScalar auto const &extents)
Allocate pinned memory on the host which is mapped into the address space of the device.
Definition Device.hpp:245
auto alloc(concepts::Device auto const &device, alpaka::concepts::VectorOrScalar auto const &extents)
Allocate memory on the given device.
Definition Device.hpp:180
constexpr auto nonBlocking
Definition tag.hpp:111
typename GetValueType< T >::type GetValueType_t
Definition trait.hpp:65
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 decltype(auto) get(concepts::SpecializationOf< Dict > auto &t) noexcept
Definition Dict.hpp:156
STL namespace.
Description of a specific device that one can schedule kernels on.
Definition Device.hpp:32
bool operator!=(Device const &other) const
Definition Device.hpp:78
bool operator==(Device const &other) const
Definition Device.hpp:73
auto makeQueue(alpaka::concepts::QueueKind auto kind)
Create a queue for this device.
Definition Device.hpp:101
constexpr auto getDeviceKind() const
Definition Device.hpp:141
auto getNativeHandle() const
Definition Device.hpp:68
void wait()
Blocks the caller until the given handle executes all work.
Definition Device.hpp:120
auto get() const
Definition Device.hpp:47
constexpr alpaka::concepts::Api auto getApi() const
Definition Device.hpp:146
size_t getFreeGlobalMemBytes() const
Definition Device.hpp:136
Device(Handle< T_Device > &&internalDeviceHandle)
Definition Device.hpp:53
std::string getName() const
Definition Device.hpp:63
typename DeviceHandle::element_type element_type
Definition Device.hpp:45
DeviceProperties getDeviceProperties() const
Properties of a given device.
Definition Device.hpp:131