buffer.h
Go to the documentation of this file.
1 #ifndef TTG_MADNESS_BUFFER_H
2 #define TTG_MADNESS_BUFFER_H
3 
5 
6 #include "ttg/device/device.h"
7 #include <memory>
8 
9 namespace ttg_madness {
10 
12 template<typename T, typename Allocator>
13 struct Buffer : private Allocator {
14 
15  using element_type = std::decay_t<T>;
16 
17  using allocator_traits = std::allocator_traits<Allocator>;
18  using allocator_type = typename allocator_traits::allocator_type;
19 
20  static_assert(std::is_trivially_copyable_v<element_type>,
21  "Only trivially copyable types are supported for devices.");
22  static_assert(std::is_default_constructible_v<element_type>,
23  "Only default constructible types are supported for devices.");
24 
25 private:
26  using delete_fn_t = std::function<void(element_type*)>;
27  using host_data_ptr = std::add_pointer_t<element_type>;
28  std::shared_ptr<element_type[]> m_sptr; // to capture smart pointers
29  host_data_ptr m_host_data = nullptr;
30  std::size_t m_count = 0;
31  bool m_owned= false;
32 
33  static void delete_non_owned(element_type *ptr) {
34  // nothing to be done, we don't own the memory
35  }
36 
37  allocator_type& get_allocator_reference() { return static_cast<allocator_type&>(*this); }
38 
39  element_type* allocate(std::size_t n) {
40  return allocator_traits::allocate(get_allocator_reference(), n);
41  }
42 
43  void deallocate() {
44  allocator_traits::deallocate(get_allocator_reference(), m_host_data, m_count);
45  }
46 
47 public:
48 
49  Buffer() : Buffer(nullptr, 0)
50  { }
51 
53  : allocator_type()
54  , m_host_data(allocate(n))
55  , m_count(n)
56  , m_owned(true)
57  { }
58 
59  /* Constructing a buffer using application-managed memory.
60  * The memory pointed to by ptr must be accessible during
61  * the life-time of the buffer. */
62  template<typename Deleter>
63  Buffer(std::unique_ptr<element_type[], Deleter> ptr, std::size_t n, ttg::scope scope = ttg::scope::SyncIn)
64  : allocator_type()
65  , m_sptr(std::move(ptr))
66  , m_host_data(m_sptr.get())
67  , m_count(n)
68  , m_owned(false)
69  { }
70 
71  /* Constructing a buffer using application-managed memory.
72  * The memory pointed to by ptr must be accessible during
73  * the life-time of the buffer. */
74  Buffer(std::shared_ptr<element_type[]> ptr, std::size_t n, ttg::scope scope = ttg::scope::SyncIn)
75  : allocator_type()
76  , m_sptr(std::move(ptr))
77  , m_host_data(m_sptr.get())
78  , m_count(n)
79  , m_owned(false)
80  { }
81 
82  virtual ~Buffer() {
83  if (m_owned) {
84  deallocate();
85  m_owned = false;
86  }
87  unpin(); // make sure the copies are not pinned
88  }
89 
90  /* allow moving device buffers */
91  Buffer(Buffer&& db)
92  : allocator_type(std::move(db))
93  , m_host_data(db.m_host_data)
94  , m_count(db.m_count)
95  , m_owned(db.m_owned)
96  {
97  db.m_host_data = nullptr;
98  db.m_count = 0;
99  db.m_owned = false;
100  }
101 
102  /* explicitly disable copying of buffers
103  * TODO: should we allow this? What data to use?
104  */
105  Buffer(const Buffer& db) = delete;
106 
107  /* allow moving device buffers */
109  allocator_type::operator=(std::move(db));
110  std::swap(m_host_data, db.m_host_data);
111  std::swap(m_count, db.m_count);
112  std::swap(m_owned, db.m_owned);
113  return *this;
114  }
115 
116  /* explicitly disable copying of buffers
117  * TODO: should we allow this? What data to use?
118  */
119  Buffer& operator=(const Buffer& db) = delete;
120 
121  /* set the current device, useful when a device
122  * buffer was modified outside of a TTG */
124  assert(is_valid());
125  if (!device.is_host()) throw std::runtime_error("MADNESS backend does not support non-host memory!");
126  /* no-op */
127  }
128 
129 
131  assert(is_valid());
132  return true;
133  }
134 
135  /* Get the owner device ID, i.e., the last updated
136  * device buffer. */
138  assert(is_valid());
139  return {}; // host only
140  }
141 
142  /* Get the pointer on the currently active device. */
144  assert(is_valid());
145  return m_host_data;
146  }
147 
148  /* Get the pointer on the currently active device. */
150  assert(is_valid());
151  return m_host_data;
152  }
153 
154  /* Get the pointer on the owning device.
155  * @note: This may not be the device assigned to the currently executing task.
156  * See \ref ttg::device::current_device for that. */
158  assert(is_valid());
159  return m_host_data;
160  }
161 
162  /* get the current device pointer */
164  assert(is_valid());
165  return m_host_data;
166  }
167 
168  /* get the device pointer at the given device
169  */
171  assert(is_valid());
172  if (device.is_device()) throw std::runtime_error("MADNESS missing support for non-host memory!");
173  return m_host_data;
174  }
175 
176  /* get the device pointer at the given device
177  */
178  const element_type* device_ptr_on(const ttg::device::Device& device) const {
179  assert(is_valid());
180  if (device.is_device()) throw std::runtime_error("MADNESS missing support for non-host memory!");
181  return m_host_data;
182  }
183 
185  return m_host_data;
186  }
187 
188  const element_type* host_ptr() const {
189  return m_host_data;
190  }
191 
192  bool is_valid_on(const ttg::device::Device& device) const {
193  assert(is_valid());
194  if (device.is_device()) throw std::runtime_error("MADNESS missing support for non-host memory!");
195  return true;
196  }
197 
198  void allocate_on(const ttg::device::Device& device_id) {
199  /* TODO: need exposed PaRSEC memory allocator */
200  throw std::runtime_error("not implemented yet");
201  }
202 
203  bool empty() const {
204  return (m_host_data == nullptr);
205  }
206 
207  /* TODO: can we do this automatically?
208  * Pin the memory on all devices we currently track.
209  * Pinned memory won't be released by PaRSEC and can be used
210  * at any time.
211  */
212  void pin() {
213  // nothing to do
214  }
215 
216  /* Unpin the memory on all devices we currently track. */
217  void unpin() {
218  // nothing to do
219  }
220 
221  /* Pin the memory on a given device */
222  void pin_on(int device_id) {
223  /* TODO: how can we pin memory on a device? */
224  }
225 
226  /* Pin the memory on a given device */
227  void unpin_on(int device_id) {
228  /* TODO: how can we unpin memory on a device? */
229  }
230 
231  bool is_valid() const {
232  return true;
233  }
234 
235  operator bool() const {
236  return true;
237  }
238 
239  std::size_t size() const {
240  return m_count;
241  }
242 
243  /* Reallocate the buffer with count elements */
244  void reset(std::size_t n, ttg::scope scope = ttg::scope::SyncIn) {
245 
246  if (m_owned) {
247  deallocate();
248  m_owned = false;
249  }
250 
251  if (n == 0) {
252  m_host_data = nullptr;
253  m_owned = false;
254  } else {
255  m_host_data = allocate(n);
256  m_owned = true;
257  }
258  m_count = n;
259  }
260 
261  /* Reallocate the buffer with count elements */
262  void reset(std::shared_ptr<element_type[]> ptr, std::size_t n, ttg::scope scope = ttg::scope::SyncIn) {
263 
264  if (m_owned) {
265  deallocate();
266  m_owned = false;
267  }
268 
269  m_sptr = std::move(ptr);
270  m_host_data = m_sptr.get();
271  m_count = n;
272  }
273 
278  /* nothing to do here */
279  }
280 
281  /* serialization support */
282 
283 #if defined(TTG_SERIALIZATION_SUPPORTS_MADNESS)
284  template <typename Archive>
285  std::enable_if_t<std::is_base_of_v<madness::archive::BufferInputArchive, Archive> ||
286  std::is_base_of_v<madness::archive::BufferOutputArchive, Archive>>
287  serialize(Archive& ar) {
288  if constexpr (ttg::detail::is_output_archive_v<Archive>) {
289  std::size_t s = size();
290  ar& s;
291  ar << madness::archive::wrap(host_ptr(), s);
292  } else {
293  std::size_t s;
294  ar & s;
295  reset(s);
296  ar >> madness::archive::wrap(host_ptr(), s); // MatrixTile<T>(bm.rows(), bm.cols());
297  }
298  }
299 #endif // TTG_SERIALIZATION_SUPPORTS_MADNESS
300 
301 
302 };
303 
304 } // namespace ttg_madness
305 
306 #endif // TTG_MADNESS_BUFFER_H
Represents a device in a specific execution space.
Definition: device.h:14
bool is_host() const
Definition: device.h:47
bool is_device() const
Definition: device.h:43
auto wrap(funcT &&func, const std::tuple< ttg::Edge< keyT, input_edge_valuesT >... > &inedges, const std::tuple< output_edgesT... > &outedges, const std::string &name="wrapper", const std::vector< std::string > &innames=std::vector< std::string >(sizeof...(input_edge_valuesT), "input"), const std::vector< std::string > &outnames=std::vector< std::string >(sizeof...(output_edgesT), "output"))
Definition: make_tt.h:590
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition: span.h:492
this contains MADNESS-based TTG functionality
Definition: fwd.h:16
scope
Definition: devicescope.h:5
A runtime-managed buffer mirrored between host and device memory.
Definition: buffer.h:13
bool empty() const
Definition: buffer.h:203
ttg::device::Device get_owner_device() const
Definition: buffer.h:137
Buffer & operator=(const Buffer &db)=delete
void reset(std::shared_ptr< element_type[]> ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition: buffer.h:262
const element_type * device_ptr_on(const ttg::device::Device &device) const
Definition: buffer.h:178
Buffer & operator=(Buffer &&db)
Definition: buffer.h:108
const element_type * current_device_ptr() const
Definition: buffer.h:149
const element_type * host_ptr() const
Definition: buffer.h:188
bool is_current_on(ttg::device::Device dev) const
Definition: buffer.h:130
virtual ~Buffer()
Definition: buffer.h:82
void unpin_on(int device_id)
Definition: buffer.h:227
Buffer(Buffer &&db)
Definition: buffer.h:91
std::decay_t< T > element_type
Definition: buffer.h:15
void reset_scope(ttg::scope scope)
Definition: buffer.h:277
element_type * device_ptr_on(const ttg::device::Device &device)
Definition: buffer.h:170
const element_type * owner_device_ptr() const
Definition: buffer.h:163
element_type * host_ptr()
Definition: buffer.h:184
void reset(std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition: buffer.h:244
Buffer(const Buffer &db)=delete
Buffer(std::shared_ptr< element_type[]> ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition: buffer.h:74
bool is_valid() const
Definition: buffer.h:231
element_type * owner_device_ptr()
Definition: buffer.h:157
element_type * current_device_ptr()
Definition: buffer.h:143
std::size_t size() const
Definition: buffer.h:239
bool is_valid_on(const ttg::device::Device &device) const
Definition: buffer.h:192
void allocate_on(const ttg::device::Device &device_id)
Definition: buffer.h:198
typename allocator_traits::allocator_type allocator_type
Definition: buffer.h:18
void pin_on(int device_id)
Definition: buffer.h:222
std::allocator_traits< Allocator > allocator_traits
Definition: buffer.h:17
Buffer(std::unique_ptr< element_type[], Deleter > ptr, std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition: buffer.h:63
Buffer(std::size_t n, ttg::scope scope=ttg::scope::SyncIn)
Definition: buffer.h:52
void set_current_device(const ttg::device::Device &device)
Definition: buffer.h:123