alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
SharedBuffer.hpp
Go to the documentation of this file.
1/* Copyright 2024 René Widera, Bernhard Manfred Gruber
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5
6#pragma once
7
8#include "alpaka/Vec.hpp"
11#include "alpaka/mem/View.hpp"
13#include "alpaka/mem/trait.hpp"
19
20#include <cstdint>
21#include <functional>
22#include <memory>
23#include <sstream>
24
25namespace alpaka::onHost
26{
27 /** Life time managed buffer with contiguous data
28 *
29 * This buffer owns the data and will deallocate it when last copy is destroyed.
30 * Const-ness of the buffer instance is propagated to the data region.
31 * A copy of this instance will only perform a shallow copy, to perform a deep copy to duplicate the data you
32 * should use @c onHost::memcpy.
33 */
34 template<
35 alpaka::concepts::Api T_Api,
36 typename T_Type,
37 alpaka::concepts::Vector T_Extents,
38 alpaka::concepts::Alignment T_MemAlignment = Alignment<>>
39 struct SharedBuffer : View<T_Api, T_Type, T_Extents, T_MemAlignment>
40 {
41 private:
43
44 /** Constructor with existing managed deleter */
46 T_Api const api,
47 T_Type* data,
48 T_Extents const& extents,
49 T_Extents const& pitches,
50 std::shared_ptr<internal::ManagedDealloc> managedDeleter,
51 T_MemAlignment const memAlignment)
52 : BaseView{api, data, extents, pitches, memAlignment}
53 , m_deleter{std::move(managedDeleter)}
54 {
55 }
56
57 // friend declaration is required that any type of SharedBuffer can access the private constructor
58 template<
59 alpaka::concepts::Api T_OtherApi,
60 typename T_OtherType,
61 alpaka::concepts::Vector T_OtherExtents,
62 alpaka::concepts::Alignment T_OtherMemAlignment2>
63 friend struct SharedBuffer;
64
65 template<
66 alpaka::concepts::Api T_OtherApi,
67 typename T_OtherType,
68 alpaka::concepts::Vector T_OtherExtents,
69 alpaka::concepts::Alignment T_OtherMemAlignment2>
70 friend std::ostream& operator<<(
71 std::ostream& s,
73
74 public:
75 template<
77 alpaka::concepts::Vector T_UserExtents,
78 alpaka::concepts::Vector T_UserPitches>
80 T_Any const& any,
81 T_Type* data,
82 T_UserExtents const& extents,
83 T_UserPitches const& pitches,
84 std::invocable<> auto deleter,
85 T_MemAlignment const memAlignment = Alignment{})
86 : BaseView{any, data, extents, pitches, memAlignment}
87 , m_deleter{std::make_shared<internal::ManagedDealloc>(deleter)}
88 {
89 static_assert(
91 "extent type and pitch type must be lossless convertible");
92 }
93
94 template<typename T_Type_Other>
95 requires alpaka::internal::concepts::InnerTypeAllowedCast<T_Type, T_Type_Other>
97 : BaseView{static_cast<BaseView>(other)}
98 , m_deleter(other.m_deleter)
99 {
100 }
101
102 SharedBuffer(SharedBuffer const&) = default;
103
104 /** Assignment operator keeping const-ness
105 *
106 * @attention the assign operator is not removing inner const-ness because the type signature is not changed.
107 */
108 SharedBuffer& operator=(SharedBuffer const& otherSharedBuffer) = default;
109
110 template<typename T_Type_Other>
111 requires alpaka::internal::concepts::InnerTypeAllowedCast<T_Type, T_Type_Other>
113 : BaseView{std::move(static_cast<BaseView>(other))}
114 , m_deleter(std::move(other.m_deleter))
115
116 {
117 }
118
120
122
123 auto getView() const
124 {
125 return BaseView::getConstView();
126 }
127
128 auto getView()
129 {
130 return static_cast<BaseView>(*this);
131 }
132
133 /** create a read shared buffer view */
135 {
136 using ConstValueType = std::add_const_t<typename BaseView::value_type>;
138 T_Api{},
139 static_cast<ConstValueType*>(this->data()),
140 this->getExtents(),
141 this->getPitches(),
142 m_deleter,
143 T_MemAlignment{});
144 }
145
146 /** Creates a buffer pointing to a part of the memory.
147 *
148 * @param extents number of elements for each dimension
149 * @return shared buffer which is pointing only to a part of the original buffer.
150 */
152 {
153 Vec extentMd = extents;
154 assert((extentMd <= this->getExtents()).reduce(std::logical_and{}));
155 return SharedBuffer<T_Api, std::remove_pointer_t<ALPAKA_TYPEOF(this->data())>, T_Extents, T_MemAlignment>{
156 T_Api{},
157 this->data(),
158 extentMd,
159 this->getPitches(),
160 m_deleter,
161 T_MemAlignment{}};
162 }
163
165 {
166 Vec extentMd = extents;
167 assert((extentMd <= this->getExtents()).reduce(std::logical_and{}));
168 return SharedBuffer<T_Api, std::remove_pointer_t<ALPAKA_TYPEOF(this->data())>, T_Extents, T_MemAlignment>{
169 T_Api{},
170 this->data(),
171 extentMd,
172 this->getPitches(),
173 m_deleter,
174 T_MemAlignment{}};
175 }
176
177 /** Creates a shared sub-buffer view to a part of the memory.
178 *
179 * @param offsets offset in elements to the original buffer
180 * @param extents number of elements for each dimension
181 * @return Buffer which is pointing only to a part of the original buffer with a shifted origin pointer.
182 * Buffer which pointThe alignment of the sub view is reduced to the element alignment.
183 */
185 alpaka::concepts::VectorOrScalar auto const& offsets,
186 alpaka::concepts::VectorOrScalar auto const& extents) const
187 {
188 Vec offsetMd = offsets;
189 Vec extentMd = extents;
190 assert((offsetMd + extentMd <= this->getExtents()).reduce(std::logical_and{}));
191 auto shiftedPtr = &(*this)[offsetMd];
192 return SharedBuffer<T_Api, std::remove_pointer_t<ALPAKA_TYPEOF(shiftedPtr)>, T_Extents, Alignment<>>{
193 T_Api{},
194 shiftedPtr,
195 extentMd,
196 this->getPitches(),
197 m_deleter,
198 Alignment<>{}};
199 }
200
202 alpaka::concepts::VectorOrScalar auto const& offsets,
203 alpaka::concepts::VectorOrScalar auto const& extents)
204 {
205 Vec offsetMd = offsets;
206 Vec extentMd = extents;
207 assert((offsetMd + extentMd <= this->getExtents()).reduce(std::logical_and{}));
208 auto shiftedPtr = &(*this)[offsetMd];
209 return SharedBuffer<T_Api, std::remove_pointer_t<ALPAKA_TYPEOF(shiftedPtr)>, T_Extents, Alignment<>>{
210 T_Api{},
211 shiftedPtr,
212 extentMd,
213 this->getPitches(),
214 m_deleter,
215 Alignment<>{}};
216 }
217
218 /** Adds a destructor action to the shared buffer
219 *
220 * The action will be executed when the buffer is destroyed.
221 * This can be used to add additional cleanup actions e.g. waiting on a specific queue.
222 * Actions are executed in FIFO order.
223 *
224 * @param action callable to execute on destruction
225 */
226 void addDestructorAction(std::function<void()>&& action)
227 {
228 m_deleter->addAction(ALPAKA_FORWARD(action));
229 }
230
231 /** Add an action to be executed when the shared_ptr is destroyed.
232 *
233 * @param action Callable to execute on destruction.
234 */
235 void destructorWaitFor(auto const& any)
236 {
237 addDestructorAction([any]() { onHost::wait(any); });
238 }
239
240 /** Keep the buffer alive until at least the current spot in the queue, even if it runs out of scope.
241 * This ensures that the buffer is and stays valid in previously enqueued kernels. There is *no* guarantee
242 * that the buffer is deleted immediately when the last reference to it is deleted.
243 *
244 * This differs from `destructorWaitFor`, because that function waits, while `keepAlive` does not block
245 * anything, it just extends lifetime.
246 *
247 * @attention Do not apply this function to a buffer allocated with alpaka::onHost::allocDeferred, see
248 * https://github.com/alpaka-group/alpaka3/issues/394
249 *
250 * @param queue The queue to enqueue to.
251 */
252 void keepAlive(auto& queue)
253 {
254 // enqueue an empty lambda that keeps a copy of the buffer
255 // as long as the copy lives (which is as long as it takes the queue to get to this point), the buffer will
256 // stay valid
257 auto del = m_deleter;
258 queue.enqueueHostFnDeferred([_ = std::move(del)] {});
259 }
260
261 /** Return the number of SharedBuffers which points to the same memory */
262 [[nodiscard]] constexpr long getUseCount() const noexcept
263 {
264 return m_deleter.use_count();
265 }
266
267 /** True if SharedBuffer is pointing to valid memory. */
268 [[nodiscard]] constexpr explicit operator bool() const noexcept
269 {
270 return static_cast<bool>(m_deleter);
271 }
272
273 private:
274 /** @todo move this to traits or somewhere else that it can be used everywhere */
275 template<alpaka::concepts::Pointer T>
276 using ConstPtr_t = std::add_pointer_t<std::add_const_t<std::remove_pointer_t<T>>>;
277
278 std::shared_ptr<internal::ManagedDealloc> m_deleter;
279 }; // namespace alpaka::onHost
280
281 template<
283 typename T_Type,
284 alpaka::concepts::Vector T_UserExtents,
285 alpaka::concepts::Vector T_UserPitches,
286 alpaka::concepts::Alignment T_MemAlignment>
288 T_Any const&,
289 T_Type*,
290 T_UserExtents const&,
291 T_UserPitches const&,
292 std::invocable<> auto,
293 T_MemAlignment const)
294 -> SharedBuffer<
295 ALPAKA_TYPEOF(getApi(std::declval<T_Any>())),
296 T_Type,
297 typename T_UserPitches::UniVec,
298 T_MemAlignment>;
299
300 template<
302 typename T_Type,
303 alpaka::concepts::Vector T_UserExtents,
304 alpaka::concepts::Vector T_UserPitches>
305 SharedBuffer(T_Any const&, T_Type*, T_UserExtents const&, T_UserPitches const&, std::invocable<> auto)
306 -> SharedBuffer<
307 ALPAKA_TYPEOF(getApi(std::declval<T_Any>())),
308 T_Type,
309 typename T_UserPitches::UniVec,
311
312 template<
314 typename T_Type,
315 alpaka::concepts::Vector T_Extents,
316 alpaka::concepts::Alignment T_MemAlignment>
317 struct MakeAccessibleOnAcc::Op<SharedBuffer<T_Api, T_Type, T_Extents, T_MemAlignment>>
318 {
319 auto operator()(auto&& any) const
320 {
321 return any.getView();
322 }
323 };
324
325 template<
327 typename T_Type,
328 alpaka::concepts::Vector T_Extents,
329 alpaka::concepts::Alignment T_MemAlignment>
330 std::ostream& operator<<(std::ostream& s, SharedBuffer<T_Api, T_Type, T_Extents, T_MemAlignment> const& buff)
331 {
332 return s << "SharedBuffer{ dim=" << ALPAKA_TYPEOF(buff)::dim() << ", api= " << onHost::getName(T_Api{})
333 << ", extents=" << buff.getExtents().toString() << ", pitches=" << buff.getPitches().toString()
334 << " , alignment=" << T_MemAlignment::template get<T_Type>() << " }";
335 }
336
337} // namespace alpaka::onHost
338
339namespace alpaka::internal
340{
341 // external define the API trait to support constexpr evaluation
342 template<
344 typename T_Type,
345 alpaka::concepts::Vector T_Extents,
346 alpaka::concepts::Alignment T_MemAlignment>
347 struct GetApi::Op<onHost::SharedBuffer<T_Api, T_Type, T_Extents, T_MemAlignment>>
348 {
349 inline constexpr auto operator()(auto&& buffer) const
350 {
351 alpaka::unused(buffer);
352 return T_Api{};
353 }
354 };
355
356 template<
357 alpaka::concepts::Api T_Api,
358 typename T_Type,
359 alpaka::concepts::Vector T_Extents,
360 alpaka::concepts::Alignment T_MemAlignment>
361 struct CopyConstructableDataSource<onHost::SharedBuffer<T_Api, T_Type, T_Extents, T_MemAlignment>> : std::true_type
362 {
363 using InnerMutable = onHost::SharedBuffer<T_Api, std::remove_const_t<T_Type>, T_Extents, T_MemAlignment>;
364 using InnerConst = onHost::SharedBuffer<T_Api, std::add_const_t<T_Type>, T_Extents, T_MemAlignment>;
365 };
366
367} // namespace alpaka::internal
#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 an alignment object.
Definition Alignment.hpp:89
Concept to check for APIs.
Definition api.hpp:42
Concept to check if the given type implements the getApi(T x) function returning an alpaka::concepts:...
Definition interface.hpp:64
Concept to check if a type is a vector or scalar variable.
Definition Vec.hpp:65
Concept to check if a type is a vector.
Definition Vec.hpp:54
Functionality which is usable on the host CPU controller thread.
Definition api.hpp:40
SharedBuffer(T_Any const &, T_Type *, T_UserExtents const &, T_UserPitches const &, std::invocable<> auto, T_MemAlignment const) -> SharedBuffer< ALPAKA_TYPEOF(getApi(std::declval< T_Any >())), T_Type, typename T_UserPitches::UniVec, T_MemAlignment >
std::ostream & operator<<(std::ostream &s, DeviceProperties const &p)
decltype(auto) data(auto &&any)
pointer to data of an object
std::convertible_to< std::string > auto getName(auto &&any)
Runtime name for a given object.
void wait(alpaka::concepts::HasGet auto &handle)
wait for all work to be finished
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
constexpr bool isLosslesslyConvertible_v
Definition trait.hpp:122
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.
Strongly typed and constexpr representation of a byte-alignment of memory.
Definition Alignment.hpp:26
constexpr auto getConstView() const
create a read only view
Definition View.hpp:150
constexpr View(T_Any const &any, T_Type *data, T_UserExtents const &extents, T_UserPitches const &pitches, T_MemAlignment const memAlignment=T_MemAlignment{})
Definition View.hpp:90
Life time managed buffer with contiguous data.
auto getSubSharedBuffer(alpaka::concepts::VectorOrScalar auto const &extents) const
Creates a buffer pointing to a part of the memory.
SharedBuffer(SharedBuffer const &)=default
constexpr long getUseCount() const noexcept
Return the number of SharedBuffers which points to the same memory.
void keepAlive(auto &queue)
Keep the buffer alive until at least the current spot in the queue, even if it runs out of scope.
SharedBuffer(T_Any const &any, T_Type *data, T_UserExtents const &extents, T_UserPitches const &pitches, std::invocable<> auto deleter, T_MemAlignment const memAlignment=Alignment{})
SharedBuffer & operator=(SharedBuffer const &otherSharedBuffer)=default
Assignment operator keeping const-ness.
void addDestructorAction(std::function< void()> &&action)
Adds a destructor action to the shared buffer.
SharedBuffer(SharedBuffer &&)=default
friend std::ostream & operator<<(std::ostream &s, SharedBuffer< T_OtherApi, T_OtherType, T_OtherExtents, T_OtherMemAlignment2 > const &buffer)
SharedBuffer(SharedBuffer< T_Api, T_Type_Other, T_Extents, T_MemAlignment > &&other)
auto getConstSharedBuffer() const
create a read shared buffer view
auto getSubSharedBuffer(alpaka::concepts::VectorOrScalar auto const &offsets, alpaka::concepts::VectorOrScalar auto const &extents)
SharedBuffer & operator=(SharedBuffer &&)=default
void destructorWaitFor(auto const &any)
Add an action to be executed when the shared_ptr is destroyed.
auto getSubSharedBuffer(alpaka::concepts::VectorOrScalar auto const &offsets, alpaka::concepts::VectorOrScalar auto const &extents) const
Creates a shared sub-buffer view to a part of the memory.
auto getSubSharedBuffer(alpaka::concepts::VectorOrScalar auto const &extents)
SharedBuffer(SharedBuffer< T_Api, T_Type_Other, T_Extents, T_MemAlignment > const &other)