ttg 1.0.0
Template Task Graph (TTG): flowgraph-based programming model for high-performance distributed-memory algorithms
Loading...
Searching...
No Matches
buffer.h
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2#ifndef TTG_PARSEC_BUFFER_H
3#define TTG_PARSEC_BUFFER_H
4
5#include <array>
6#include <vector>
7#include <cassert>
8#include <parsec.h>
9#include <parsec/data_internal.h>
10#include <parsec/mca/device/device.h>
11#include <parsec/mca/device/device_gpu.h>
12#include <parsec/utils/zone_malloc.h>
15#include "ttg/util/iovec.h"
16#include "ttg/device/device.h"
17#include "ttg/parsec/device.h"
18#include "ttg/devicescope.h"
19
20#if defined(PARSEC_HAVE_DEV_CUDA_SUPPORT)
21#include <cuda_runtime.h>
22#endif // PARSEC_HAVE_DEV_CUDA_SUPPORT
23
24namespace ttg_parsec {
25
26
27namespace detail {
28 // fwd decl
29 template<typename T, typename A>
30 parsec_data_t* get_parsec_data(const ttg_parsec::Buffer<T, A>& db);
31
32 template<typename T>
34
35 using value_type = std::decay_t<T>;
36
37 value_type* allocate(std::size_t size) {
38 throw std::runtime_error("Allocate on empty allocator!");
39 }
40
41 void deallocate(value_type* ptr, std::size_t size) {
42 /* nothing to be done; will be called from ~data_copy_type() */
43 }
44 };
45
46 /* overloads for pointers and smart pointers */
47 template<typename T>
48 inline T* to_address(T* ptr) {
49 return ptr;
50 }
51
52 template<typename T>
53 inline auto to_address(T&& ptr) {
54 return ptr.get(); // smart pointer
55 }
56
61 template<typename PtrT, typename Allocator>
63 using allocator_traits = std::allocator_traits<Allocator>;
64 using allocator_type = typename allocator_traits::allocator_type;
65 using value_type = typename allocator_traits::value_type;
66
67 /* always allocate host memory if the host is the only execution space available */
68 static constexpr bool always_allocate_on_host
70
71 /* used as a hook into the PaRSEC object management system
72 * so we can release the memory back to the allocator once
73 * data copy is destroyed */
74 struct data_copy_type : public parsec_data_copy_t
75 {
76 private:
77 [[no_unique_address]]
78 allocator_type m_allocator;
79 PtrT m_ptr;
80 std::size_t m_size;
81
82 void do_allocate() {
83 /* some allocators may throw if they are out of memory */
84 try {
85 m_ptr = std::shared_ptr<value_type[]>(allocator_traits::allocate(m_allocator, m_size),
86 [allocator = m_allocator, size = m_size](value_type* ptr) mutable {
87 allocator_traits::deallocate(allocator, ptr, size);
88 });
89 } catch(...) {
90 /* fall-back to regular memory if the allocator runs dry */
91 m_ptr = std::make_shared<value_type[]>(m_size);
92 }
93 this->device_private = m_ptr.get();
94 }
95
96 void do_deallocate() {
97 if (this->device_private != nullptr) {
98 auto ptr = std::move(m_ptr);
99 this->device_private = nullptr;
100 this->version = 0;
101 this->coherency_state = PARSEC_DATA_COHERENCY_INVALID;
102 ptr.reset(); // deallocate the shared pointer
103 }
104 }
105
106 static void allocate(parsec_data_copy_t *parsec_copy, int device) {
107 data_copy_type* copy = static_cast<data_copy_type*>(parsec_copy);
108 copy->do_allocate();
109 }
110
111 static void deallocate(parsec_data_copy_t *parsec_copy, int device) {
112 data_copy_type* copy = static_cast<data_copy_type*>(parsec_copy);
113 copy->do_deallocate();
114 }
115
116 public:
117
118 /* default construction and move, but not copy */
119 data_copy_type() = default;
124
125 template<typename Ptr>
126 void construct(Ptr&& ptr, std::size_t size) {
127 m_allocator = allocator_type{};
128 constexpr const bool is_empty_allocator = std::is_same_v<Allocator, empty_allocator<value_type>>;
129 assert(is_empty_allocator);
130 m_ptr = std::move(ptr);
131 this->m_size = size;
132 this->dtt = parsec_datatype_int8_t;
133 this->device_private = const_cast<value_type*>(to_address(m_ptr));
134 }
135
136 template<typename AllocatorT = allocator_type>
137 void construct(std::size_t size,
138 ttg::scope scope,
139 AllocatorT&& alloc = AllocatorT()) {
140 constexpr const bool is_empty_allocator = std::is_same_v<AllocatorT, empty_allocator<value_type>>;
141 assert(!is_empty_allocator);
142 m_allocator = std::forward<AllocatorT>(alloc);
143 this->m_size = size;
144 this->dtt = parsec_datatype_int8_t;
146 /* the user requested that the data be sync'ed into the device
147 * so we need to provide host memory for the user to fill prior */
148 do_allocate();
149 } else {
150 /* if the user only requests an allocation on the device
151 * we don't allocate host memory but provide PaRSEC with
152 * a way to request host memory from us. */
153 this->alloc_cb = &allocate;
154 this->release_cb = &deallocate;
155 this->device_private = nullptr;
156 }
157 }
158
160 this->alloc_cb = nullptr;
161 this->release_cb = nullptr;
162 this->do_deallocate();
163 }
164 };
165
170 {
171 /* placement new */
172 new(obj)(data_copy_type);
173 }
174
176 {
177 obj->~data_copy_type(); // call destructor
178 }
179
180 inline static PARSEC_OBJ_CLASS_INSTANCE(data_copy_type, parsec_data_copy_t,
183
184 static parsec_data_t * create_data(std::size_t size, ttg::scope scope,
185 const allocator_type& allocator = allocator_type()) {
186 parsec_data_t *data = PARSEC_OBJ_NEW(parsec_data_t);
187 data->owner_device = 0;
188 data->nb_elts = size*sizeof(value_type);
189
190 /* create the host copy and allocate host memory */
191 data_copy_type *copy = PARSEC_OBJ_NEW(data_copy_type);
192 copy->construct(size, scope, allocator);
193 parsec_data_copy_attach(data, copy, 0);
194
195 /* adjust data flags */
196 data->device_copies[0]->flags |= PARSEC_DATA_FLAG_PARSEC_MANAGED;
198 data->device_copies[0]->coherency_state = PARSEC_DATA_COHERENCY_EXCLUSIVE;
199 data->device_copies[0]->version = 1;
200 } else {
201 data->device_copies[0]->coherency_state = PARSEC_DATA_COHERENCY_INVALID;
202 /* setting version to 0 causes data not to be sent to the device */
203 data->device_copies[0]->version = 0;
204 }
205
206 return data;
207 }
208
209 static parsec_data_t * create_data(PtrT& ptr, std::size_t size, ttg::scope scope) {
210 parsec_data_t *data = PARSEC_OBJ_NEW(parsec_data_t);
211 data->owner_device = 0;
212 data->nb_elts = size*sizeof(value_type);
213
214 /* create the host copy and allocate host memory */
215 data_copy_type *copy = PARSEC_OBJ_NEW(data_copy_type);
216 copy->construct(std::move(ptr), size);
217 parsec_data_copy_attach(data, copy, 0);
218
219 /* adjust data flags */
220 data->device_copies[0]->flags |= PARSEC_DATA_FLAG_PARSEC_MANAGED;
222 data->device_copies[0]->coherency_state = PARSEC_DATA_COHERENCY_EXCLUSIVE;
223 data->device_copies[0]->version = 1;
224 } else {
225 data->device_copies[0]->coherency_state = PARSEC_DATA_COHERENCY_INVALID;
226 /* setting version to 0 causes data not to be sent to the device */
227 data->device_copies[0]->version = 0;
228 }
229
230 return data;
231 }
232 };
233} // namespace detail
234
246template<typename T, typename Allocator>
247struct Buffer {
248
249 /* TODO: add overloads for T[]? */
250 using value_type = std::remove_all_extents_t<T>;
251 using pointer_type = std::add_pointer_t<value_type>;
252 using const_pointer_type = const std::remove_const_t<value_type>*;
253 using element_type = std::decay_t<T>;
254
255 static_assert(std::is_trivially_copyable_v<element_type>,
256 "Only trivially copyable types are supported for devices.");
257
258private:
259 parsec_data_t *m_data = nullptr;
260 std::size_t m_count = 0;
261
262 friend parsec_data_t* detail::get_parsec_data<T>(const ttg_parsec::Buffer<T, Allocator>&);
263
264
265 void release_data() {
266 if (nullptr == m_data) return;
267 /* discard the parsec data so it can be collected by the runtime
268 * and the buffer be free'd in the parsec_data_copy_t destructor */
269 parsec_data_discard(m_data);
270 /* set data to null so we don't repeat the above */
271 m_data = nullptr;
272 }
273
274public:
275
276 Buffer() = default;
277
279 : m_data(detail::ttg_parsec_data_types<std::shared_ptr<value_type[]>, Allocator>::create_data(n, scope))
280 , m_count(n)
281 { }
282
288 Buffer(std::shared_ptr<value_type[]> ptr, std::size_t n,
290 : m_data(detail::ttg_parsec_data_types<std::shared_ptr<value_type[]>,
291 detail::empty_allocator<element_type>>
292 ::create_data(ptr, n, scope))
293 , m_count(n)
294 { }
295
296 template<typename Deleter>
297 Buffer(std::unique_ptr<value_type[], Deleter> ptr, std::size_t n,
299 : m_data(detail::ttg_parsec_data_types<std::unique_ptr<value_type[], Deleter>,
300 detail::empty_allocator<element_type>>
301 ::create_data(ptr, n, scope))
302 , m_count(n)
303 { }
304
305 virtual ~Buffer() {
306 unpin(); // make sure the copies are not pinned
307 release_data();
308 }
309
310 /* allow moving device buffers */
312 : m_data(db.m_data)
313 , m_count(db.m_count)
314 {
315 db.m_data = nullptr;
316 db.m_count = 0;
317 }
318
319 /* explicitly disable copying of buffers
320 * TODO: should we allow this? What data to use?
321 */
322 Buffer(const Buffer& db) = delete;
323
324 /* allow moving device buffers */
326 std::swap(m_data, db.m_data);
327 std::swap(m_count, db.m_count);
328 //std::cout << "buffer " << this << " other " << &db << " mv op ttg_copy " << m_ttg_copy << std::endl;
329 //std::cout << "buffer::move-assign from " << &db << " ttg-copy " << db.m_ttg_copy
330 // << " to " << this << " ttg-copy " << m_ttg_copy
331 // << " parsec-data " << m_data.get()
332 // << std::endl;
333 /* don't update the ttg_copy, we keep the connection */
334 return *this;
335 }
336
337 /* explicitly disable copying of buffers
338 * TODO: should we allow this? What data to use?
339 */
340 Buffer& operator=(const Buffer& db) = delete;
341
342 /* set the current device, useful when a device
343 * buffer was modified outside of a TTG */
345 assert(is_valid());
346 int parsec_id = detail::ttg_device_to_parsec_device(device);
347 /* make sure it's a valid device */
348 assert(parsec_nb_devices > parsec_id);
349 /* make sure it's a valid copy */
350 assert(m_data->device_copies[parsec_id] != nullptr);
351 m_data->owner_device = parsec_id;
352 m_data->device_copies[parsec_id]->version = m_data->device_copies[0]->version;
353 m_data->device_copies[parsec_id]->coherency_state = PARSEC_DATA_COHERENCY_EXCLUSIVE;
354 }
355
357 if (empty()) return true; // empty is current everywhere
358 int parsec_id = detail::ttg_device_to_parsec_device(dev);
359 uint32_t max_version = 0;
360 for (int i = 0; i < parsec_nb_devices; ++i) {
361 if (nullptr == m_data->device_copies[i]) continue;
362 max_version = std::max(max_version, m_data->device_copies[i]->version);
363 }
364 return (m_data->device_copies[parsec_id] &&
365 m_data->device_copies[parsec_id]->version == max_version);
366 }
367
368 /* Get the owner device ID, i.e., the last updated
369 * device buffer.
370 * NOTE: there may be more than one device with the current
371 * data so the result may not always be what is expected.
372 * Use is_current_on() to check for a specific device. */
374 assert(is_valid());
375 if (empty()) return ttg::device::current_device(); // empty is always valid
376 return detail::parsec_device_to_ttg_device(m_data->owner_device);
377 }
378
379 /* Get the pointer on the currently active device. */
381 assert(is_valid());
382 if (empty()) return nullptr;
384 return static_cast<pointer_type>(m_data->device_copies[device_id]->device_private);
385 }
386
387 /* Get the pointer on the currently active device. */
389 assert(is_valid());
390 if (empty()) return nullptr;
392 return static_cast<const_pointer_type>(m_data->device_copies[device_id]->device_private);
393 }
394
395 /* Get the pointer on the owning device.
396 * @note: This may not be the device assigned to the currently executing task.
397 * See \ref ttg::device::current_device for that. */
399 assert(is_valid());
400 if (empty()) return nullptr;
401 return static_cast<pointer_type>(m_data->device_copies[m_data->owner_device]->device_private);
402 }
403
404 /* get the current device pointer */
406 assert(is_valid());
407 if (empty()) return nullptr;
408 return static_cast<const_pointer_type>(m_data->device_copies[m_data->owner_device]->device_private);
409 }
410
411 /* get the device pointer at the given device
412 */
414 assert(is_valid());
415 if (empty()) return nullptr;
416 int device_id = detail::ttg_device_to_parsec_device(device);
417 return static_cast<pointer_type>(parsec_data_get_ptr(m_data, device_id));
418 }
419
420 /* get the device pointer at the given device
421 */
423 assert(is_valid());
424 if (empty()) return nullptr;
425 int device_id = detail::ttg_device_to_parsec_device(device);
426 return static_cast<const_pointer_type>(parsec_data_get_ptr(m_data, device_id));
427 }
428
430 return static_cast<pointer_type>(parsec_data_get_ptr(m_data, 0));
431 }
432
434 return static_cast<const_pointer_type>(parsec_data_get_ptr(m_data, 0));
435 }
436
437 bool is_valid_on(const ttg::device::Device& device) const {
438 assert(is_valid());
439 int device_id = detail::ttg_device_to_parsec_device(device);
440 return (parsec_data_get_ptr(m_data, device_id) != nullptr);
441 }
442
443 void allocate_on(const ttg::device::Device& device) {
444 if (is_valid_on(device)) return; // already allocated
445 if (!m_data) throw std::runtime_error("Cannot allocate on an empty buffer!");
446 int parsec_id = detail::ttg_device_to_parsec_device(device);
447 assert(parsec_nb_devices > parsec_id);
448 assert(m_data != nullptr);
449 if (m_data->device_copies[parsec_id] == nullptr || m_data->device_copies[parsec_id]->device_private == nullptr) {
450 if (device.is_device()) {
451 /* create the device copy */
452 parsec_device_gpu_module_t *device_module = (parsec_device_gpu_module_t*)parsec_mca_device_get(parsec_id);
453 /* thread-safe allocation from PaRSEC */
454 T* ptr = (T*)zone_malloc(device_module->memory, m_count*sizeof(T));
455 if (nullptr == ptr) {
456 throw std::bad_alloc{};
457 }
458 parsec_data_copy_t* copy = nullptr;
459 if (m_data->device_copies[parsec_id] == nullptr) {
460 /* transfer ownership of the memory to PaRSEC (so it can be pushed out if needed) */
461 copy = parsec_data_copy_new(m_data, parsec_id, parsec_datatype_int8_t,
462 PARSEC_DATA_FLAG_PARSEC_MANAGED | PARSEC_DATA_FLAG_PARSEC_OWNED);
463 } else {
464 copy = m_data->device_copies[parsec_id];
465 }
466 copy->coherency_state = PARSEC_DATA_COHERENCY_INVALID;
467 copy->device_private = ptr;
468 m_data->device_copies[parsec_id] = copy;
469 } else {
470 reset(m_count, ttg::scope::SyncIn);
471 }
472 m_data->device_copies[parsec_id]->version = 0;
473 }
474 }
475
476 /* TODO: can we do this automatically?
477 * Pin the memory on all devices we currently track.
478 * Pinned memory won't be released by PaRSEC and can be used
479 * at any time.
480 */
481 void pin() {
482 for (int i = 1; i < parsec_nb_devices; ++i) {
483 pin_on(i);
484 }
485 }
486
487 /* Unpin the memory on all devices we currently track. */
488 void unpin() {
489 if (!is_valid()) return;
490 for (int i = 0; i < parsec_nb_devices-detail::first_device_id; ++i) {
491 unpin_on(i);
492 }
493 }
494
495 /* Pin the memory on a given device */
496 void pin_on(int device_id) {
497 /* TODO: how can we pin memory on a device? */
498 }
499
500 /* Pin the memory on a given device */
501 void unpin_on(int device_id) {
502 /* TODO: how can we unpin memory on a device? */
503 }
504
505 bool is_valid() const {
506 return (m_count == 0 || m_data);
507 }
508
509 operator bool() const {
510 return !empty();
511 }
512
513 std::size_t size() const {
514 return m_count;
515 }
516
517 bool empty() const {
518 return m_count == 0;
519 }
520
521 /* Reallocate the buffer with count elements */
522 void reset(std::size_t n, ttg::scope scope = ttg::scope::SyncIn) {
523 release_data();
524 m_data = detail::ttg_parsec_data_types<std::shared_ptr<value_type[]>, Allocator>::create_data(n, scope);
525 m_count = n;
526 }
527
528 /* Reallocate the buffer with count elements */
529 void reset(std::shared_ptr<value_type[]> ptr, std::size_t n, ttg::scope scope = ttg::scope::SyncIn) {
530 release_data();
533 ::create_data(ptr, n, scope);
534 m_count = n;
535 }
536
540 void clear() {
541 release_data();
542 m_count = 0;
543 }
544
554 m_data->device_copies[0]->version = 0;
555 } else if (scope == ttg::scope::SyncIn) {
556 m_data->device_copies[0]->version = 1;
557 /* reset all other copies to force a sync-in */
558 for (int i = 0; i < parsec_nb_devices; ++i) {
559 if (m_data->device_copies[i] != nullptr) {
560 m_data->device_copies[i]->version = 0;
561 }
562 }
563 }
564 m_data->owner_device = 0;
565 }
566
568 /* if the host owns the data and has a version of zero we only have to allocate data */
569 if (nullptr == m_data) return ttg::scope::Invalid;
570 return (m_data->device_copies[0]->version == 0)
572 }
573
575 /* only set device if the host has the latest copy as otherwise we might end up with a stale copy */
576 if (dev.is_device() && m_data->owner_device == 0) {
577 parsec_advise_data_on_device(m_data, detail::ttg_device_to_parsec_device(dev),
578 PARSEC_DEV_DATA_ADVICE_PREFERRED_DEVICE);
579 }
580 }
581
582 void add_device(ttg::device::Device dev, pointer_type ptr, bool is_current = false) {
583 if (is_valid_on(dev)) {
584 throw std::runtime_error("Unable to add device that has already a buffer set!");
585 }
587 if (is_current) {
588 // mark the data as being current on the new device
589 m_data->owner_device = detail::ttg_device_to_parsec_device(dev);
590 }
591 }
592
593 /* serialization support */
594
595#ifdef TTG_SERIALIZATION_SUPPORTS_BOOST
596 template <typename Archive>
597 void serialize(Archive& ar, const unsigned int version) {
598 if constexpr (ttg::detail::is_output_archive_v<Archive>) {
599 std::size_t s = size();
600 ar& s;
601 } else {
602 std::size_t s;
603 ar & s;
604 /* initialize internal pointers and then reset */
605 reset(s);
606 }
607 }
608#endif // TTG_SERIALIZATION_SUPPORTS_BOOST
609
610#ifdef TTG_SERIALIZATION_SUPPORTS_MADNESS
611 template <typename Archive>
612 std::enable_if_t<std::is_base_of_v<madness::archive::BufferInputArchive, Archive> ||
613 std::is_base_of_v<madness::archive::BufferOutputArchive, Archive>>
614 serialize(Archive& ar) {
615 if constexpr (ttg::detail::is_output_archive_v<Archive>) {
616 std::size_t s = size();
617 ar& s;
618 } else {
619 std::size_t s;
620 ar & s;
621 /* if we have been initialized already we just make sure the size matches */
622 if (m_data != nullptr) {
623 if (s != size()) {
624 throw std::runtime_error("Buffer size mismatch in deserialization!");
625 }
626 } else {
627 //std::cout << "serialize(IN) buffer " << this << " size " << s << std::endl;
628 /* initialize but don't force allocation on the host */
630 }
631 }
632 }
633#endif // TTG_SERIALIZATION_SUPPORTS_MADNESS
634};
635
636namespace detail {
637 template<typename T, typename A>
638 parsec_data_t* get_parsec_data(const ttg_parsec::Buffer<T, A>& db) {
639 return const_cast<parsec_data_t*>(db.m_data);
640 }
641} // namespace detail
642
643} // namespace ttg_parsec
644
645#endif // TTG_PARSEC_BUFFER_H
Represents a device in a specific execution space.
Definition device.h:19
bool is_device() const
Definition device.h:52
STL namespace.
Device current_device()
Definition device.h:156
constexpr ttg::ExecutionSpace available_execution_space
Definition device.h:133
ttg::device::Device parsec_device_to_ttg_device(int parsec_id)
Definition device.h:31
int ttg_device_to_parsec_device(const ttg::device::Device &device)
Definition device.h:19
T * to_address(T *ptr)
Definition buffer.h:48
parsec_data_t * get_parsec_data(const ttg_parsec::Buffer< T, A > &db)
Definition buffer.h:638
this contains PaRSEC-based TTG functionality
Definition fwd.h:19
std::decay_t< T > element_type
Definition buffer.h:253
Buffer(Buffer &&db)
Definition buffer.h:311
std::add_pointer_t< value_type > pointer_type
Definition buffer.h:251
const_pointer_type device_ptr_on(const ttg::device::Device &device) const
Definition buffer.h:422
void prefer_device(ttg::device::Device dev)
Definition buffer.h:574
std::size_t size() const
Definition buffer.h:513
const_pointer_type owner_device_ptr() const
Definition buffer.h:405
bool is_valid() const
Definition buffer.h:505
void reset(std::shared_ptr< value_type[]> ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition buffer.h:529
virtual ~Buffer()
Definition buffer.h:305
Buffer & operator=(const Buffer &db)=delete
bool is_valid_on(const ttg::device::Device &device) const
Definition buffer.h:437
pointer_type host_ptr()
Definition buffer.h:429
Buffer(const Buffer &db)=delete
ttg::device::Device get_owner_device() const
Definition buffer.h:373
void reset_scope(ttg::scope scope)
Definition buffer.h:552
pointer_type device_ptr_on(const ttg::device::Device &device)
Definition buffer.h:413
const_pointer_type host_ptr() const
Definition buffer.h:433
Buffer(std::unique_ptr< value_type[], Deleter > ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition buffer.h:297
const_pointer_type current_device_ptr() const
Definition buffer.h:388
Buffer & operator=(Buffer &&db)
Definition buffer.h:325
void add_device(ttg::device::Device dev, pointer_type ptr, bool is_current=false)
Definition buffer.h:582
void pin_on(int device_id)
Definition buffer.h:496
Buffer(std::shared_ptr< value_type[]> ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition buffer.h:288
bool is_current_on(ttg::device::Device dev) const
Definition buffer.h:356
std::remove_all_extents_t< T > value_type
Definition buffer.h:250
void allocate_on(const ttg::device::Device &device)
Definition buffer.h:443
ttg::scope scope() const
Definition buffer.h:567
bool empty() const
Definition buffer.h:517
pointer_type owner_device_ptr()
Definition buffer.h:398
void set_owner_device(const ttg::device::Device &device)
Definition buffer.h:344
const std::remove_const_t< value_type > * const_pointer_type
Definition buffer.h:252
Buffer(std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition buffer.h:278
pointer_type current_device_ptr()
Definition buffer.h:380
void reset(std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition buffer.h:522
void unpin_on(int device_id)
Definition buffer.h:501
std::decay_t< T > value_type
Definition buffer.h:35
value_type * allocate(std::size_t size)
Definition buffer.h:37
void deallocate(value_type *ptr, std::size_t size)
Definition buffer.h:41
data_copy_type & operator=(data_copy_type &&)=default
void construct(Ptr &&ptr, std::size_t size)
Definition buffer.h:126
void construct(std::size_t size, ttg::scope scope, AllocatorT &&alloc=AllocatorT())
Definition buffer.h:137
data_copy_type & operator=(const data_copy_type &)=delete
static parsec_data_t * create_data(PtrT &ptr, std::size_t size, ttg::scope scope)
Definition buffer.h:209
static parsec_data_t * create_data(std::size_t size, ttg::scope scope, const allocator_type &allocator=allocator_type())
Definition buffer.h:184
static PARSEC_OBJ_CLASS_INSTANCE(data_copy_type, parsec_data_copy_t, data_copy_construct, data_copy_destruct)
static void data_copy_destruct(data_copy_type *obj)
Definition buffer.h:175
static constexpr bool always_allocate_on_host
Definition buffer.h:69
typename allocator_traits::value_type value_type
Definition buffer.h:65
static void data_copy_construct(data_copy_type *obj)
Definition buffer.h:169
typename allocator_traits::allocator_type allocator_type
Definition buffer.h:64
std::allocator_traits< Allocator > allocator_traits
Definition buffer.h:63