MFEM  v4.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
mem_manager.hpp
Go to the documentation of this file.
1 // Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
3 // reserved. See file COPYRIGHT for details.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability see http://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the GNU Lesser General Public License (as published by the Free
10 // Software Foundation) version 2.1 dated February 1999.
11 
12 #ifndef MFEM_MEM_MANAGER_HPP
13 #define MFEM_MEM_MANAGER_HPP
14 
15 #include "globals.hpp"
16 #include "error.hpp"
17 #include <cstring> // std::memcpy
18 #include <type_traits> // std::is_const
19 
20 namespace mfem
21 {
22 
23 // Implementation of MFEM's lightweight device/host memory manager designed to
24 // work seamlessly with the OCCA, RAJA, and other kernels supported by MFEM.
25 
26 /// Memory types supported by MFEM.
27 enum class MemoryType
28 {
29  HOST, ///< Host memory; using new[] and delete[]
30  HOST_32, ///< Host memory aligned at 32 bytes (not supported yet)
31  HOST_64, ///< Host memory aligned at 64 bytes (not supported yet)
32  CUDA, ///< cudaMalloc, cudaFree
33  CUDA_UVM ///< cudaMallocManaged, cudaFree (not supported yet)
34 };
35 
36 /// Memory classes identify subsets of memory types.
37 /** This type is used by kernels that can work with multiple MemoryType%s. For
38  example, kernels that can use CUDA or CUDA_UVM memory types should use
39  MemoryClass::CUDA for their inputs. */
40 enum class MemoryClass
41 {
42  HOST, ///< Memory types: { HOST, HOST_32, HOST_64, CUDA_UVM }
43  HOST_32, ///< Memory types: { HOST_32, HOST_64 }
44  HOST_64, ///< Memory types: { HOST_64 }
45  CUDA, ///< Memory types: { CUDA, CUDA_UVM }
46  CUDA_UVM ///< Memory types: { CUDA_UVM }
47 };
48 
49 /// Return true if the given memory type is in MemoryClass::HOST.
50 inline bool IsHostMemory(MemoryType mt) { return mt <= MemoryType::HOST_64; }
51 
52 /// Return a suitable MemoryType for a given MemoryClass.
54 
55 /// Return a suitable MemoryClass from a pair of MemoryClass%es.
56 /** Note: this operation is commutative, i.e. a*b = b*a, associative, i.e.
57  (a*b)*c = a*(b*c), and has an identity element: MemoryClass::HOST.
58 
59  Currently, the operation is defined as a*b := max(a,b) where the max
60  operation is based on the enumeration ordering:
61 
62  HOST < HOST_32 < HOST_64 < CUDA < CUDA_UVM. */
64 
65 /// Class used by MFEM to store pointers to host and/or device memory.
66 /** The template class parameter, T, must be a plain-old-data (POD) type.
67 
68  In many respects this class behaves like a pointer:
69  * When destroyed, a Memory object does NOT automatically delete any
70  allocated memory.
71  * Only the method Delete() will deallocate a Memory object.
72  * Other methods that modify the object (e.g. New(), Wrap(), etc) will simply
73  overwrite the old contents.
74  * One difference with a pointer is that a const Memory object does not allow
75  modification of the content (unlike e.g. a const pointer).
76 
77  A Memory object stores up to two different pointers: one host pointer (with
78  MemoryType from MemoryClass::HOST) and one device pointer (currently one of
79  MemoryType::CUDA or MemoryTyep::CUDA_UVM).
80 
81  A Memory object can hold (wrap) an externally allocated pointer with any
82  given MemoryType.
83 
84  Access to the content of the Memory object can be requested with any given
85  MemoryClass through the methods ReadWrite(), Read(), and Write().
86  Requesting such access may result in additional (internally handled)
87  memory allocation and/or memory copy.
88  * When ReadWrite() is called, the returned pointer becomes the only
89  valid pointer.
90  * When Read() is called, the returned pointer becomes valid, however
91  the other pointer (host or device) may remain valid as well.
92  * When Write() is called, the returned pointer becomes the only valid
93  pointer, however, unlike ReadWrite(), no memory copy will be performed.
94 
95  The host memory (pointer from MemoryClass::HOST) can be accessed through the
96  inline methods: `operator[]()`, `operator*()`, the implicit conversion
97  functions `operator T*()`, `operator const T*()`, and the explicit
98  conversion template functions `operator U*()`, `operator const U*()` (with
99  any suitable type U). In certain cases, using these methods may have
100  undefined behavior, e.g. if the host pointer is not currently valid. */
101 template <typename T>
102 class Memory
103 {
104 protected:
105  friend class MemoryManager;
106  friend void MemoryPrintFlags(unsigned flags);
107 
108  enum FlagMask
109  {
110  REGISTERED = 1, ///< #h_ptr is registered with the MemoryManager
111  OWNS_HOST = 2, ///< The host pointer will be deleted by Delete()
112  OWNS_DEVICE = 4, ///< The device pointer will be deleted by Delete()
113  OWNS_INTERNAL = 8, ///< Ownership flag for internal Memory data
114  VALID_HOST = 16, ///< Host pointer is valid
115  VALID_DEVICE = 32, ///< Device pointer is valid
116  ALIAS = 64,
117  /// Internal device flag, see e.g. Vector::UseDevice()
119  };
120 
121  /// Pointer to host memory. Not owned.
122  /** When the pointer is not registered with the MemoryManager, this pointer
123  has type MemoryType::HOST. When the pointer is registered, it can be any
124  type from MemoryClass::HOST. */
125  T *h_ptr;
126  int capacity;
127  mutable unsigned flags;
128  // 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
129  // Copy{From,To}, {ReadWrite,Read,Write}.
130 
131 public:
132  /// Default constructor: no initialization.
133  Memory() { }
134 
135  /// Copy constructor: default.
136  Memory(const Memory &orig) = default;
137 
138  /// Move constructor: default.
139  Memory(Memory &&orig) = default;
140 
141  /// Copy-assignment operator: default.
142  Memory &operator=(const Memory &orig) = default;
143 
144  /// Move-assignment operator: default.
145  Memory &operator=(Memory &&orig) = default;
146 
147  /// Allocate host memory for @a size entries.
148  explicit Memory(int size) { New(size); }
149 
150  /** @brief Allocate memory for @a size entries with the given MemoryType
151  @a mt. */
152  /** The newly allocated memory is not initialized, however the given
153  MemoryType is still set as valid. */
154  Memory(int size, MemoryType mt) { New(size, mt); }
155 
156  /** @brief Wrap an externally allocated host pointer, @a ptr with type
157  MemoryType::HOST. */
158  /** The parameter @a own determines whether @a ptr will be deleted (using
159  operator delete[]) when the method Delete() is called. */
160  explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
161 
162  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
163  /** The new memory object will have the given MemoryType set as valid.
164 
165  The given @a ptr must be allocated appropriately for the given
166  MemoryType.
167 
168  The parameter @a own determines whether @a ptr will be deleted when the
169  method Delete() is called. */
170  Memory(T *ptr, int size, MemoryType mt, bool own)
171  { Wrap(ptr, size, mt, own); }
172 
173  /** @brief Alias constructor. Create a Memory object that points inside the
174  Memory object @a base. */
175  /** The new Memory object uses the same MemoryType(s) as @a base. */
176  Memory(const Memory &base, int offset, int size)
177  { MakeAlias(base, offset, size); }
178 
179  /// Destructor: default.
180  /** @note The destructor will NOT delete the current memory. */
181  ~Memory() = default;
182 
183  /** @brief Return true if the host pointer is owned. Ownership indicates
184  whether the pointer will be deleted by the method Delete(). */
185  bool OwnsHostPtr() const { return flags & OWNS_HOST; }
186 
187  /** @brief Set/clear the ownership flag for the host pointer. Ownership
188  indicates whether the pointer will be deleted by the method Delete(). */
189  void SetHostPtrOwner(bool own) const
190  { flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
191 
192  /** @brief Return true if the device pointer is owned. Ownership indicates
193  whether the pointer will be deleted by the method Delete(). */
194  bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
195 
196  /** @brief Set/clear the ownership flag for the device pointer. Ownership
197  indicates whether the pointer will be deleted by the method Delete(). */
198  void SetDevicePtrOwner(bool own) const
199  { flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
200 
201  /** @brief Clear the ownership flags for the host and device pointers, as
202  well as any internal data allocated by the Memory object. */
203  void ClearOwnerFlags() const
205 
206  /// Read the internal device flag.
207  bool UseDevice() const { return flags & USE_DEVICE; }
208 
209  /// Set the internal device flag.
210  void UseDevice(bool use_dev) const
211  { flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
212 
213  /// Return the size of the allocated memory.
214  int Capacity() const { return capacity; }
215 
216  /// Reset the memory to be empty, ensuring that Delete() will be a no-op.
217  /** This is the Memory class equivalent to setting a pointer to NULL, see
218  Empty().
219 
220  @note The current memory is NOT deleted by this method. */
221  void Reset() { h_ptr = NULL; capacity = 0; flags = 0; }
222 
223  /// Return true if the Memory object is empty, see Reset().
224  /** Default-constructed objects are uninitialized, so they are not guaranteed
225  to be empty. */
226  bool Empty() const { return h_ptr == NULL; }
227 
228  /// Allocate host memory for @a size entries with type MemoryType::HOST.
229  /** @note The current memory is NOT deleted by this method. */
230  void New(int size)
231  { h_ptr = new T[size]; capacity = size; flags = OWNS_HOST | VALID_HOST; }
232 
233  /// Allocate memory for @a size entries with the given MemoryType.
234  /** The newly allocated memory is not initialized, however the given
235  MemoryType is still set as valid.
236 
237  @note The current memory is NOT deleted by this method. */
238  inline void New(int size, MemoryType mt);
239 
240  /** @brief Wrap an externally allocated host pointer, @a ptr with type
241  MemoryType::HOST. */
242  /** The parameter @a own determines whether @a ptr will be deleted (using
243  operator delete[]) when the method Delete() is called.
244 
245  @note The current memory is NOT deleted by this method. */
246  inline void Wrap(T *ptr, int size, bool own)
247  { h_ptr = ptr; capacity = size; flags = (own ? OWNS_HOST : 0) | VALID_HOST; }
248 
249  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
250  /** The new memory object will have the given MemoryType set as valid.
251 
252  The given @a ptr must be allocated appropriately for the given
253  MemoryType.
254 
255  The parameter @a own determines whether @a ptr will be deleted when the
256  method Delete() is called.
257 
258  @note The current memory is NOT deleted by this method. */
259  inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
260 
261  /// Create a memory object that points inside the memory object @a base.
262  /** The new Memory object uses the same MemoryType(s) as @a base.
263 
264  @note The current memory is NOT deleted by this method. */
265  inline void MakeAlias(const Memory &base, int offset, int size);
266 
267  /// Delete the owned pointers. The Memory is not reset by this method.
268  inline void Delete();
269 
270  /// Array subscript operator for host memory.
271  inline T &operator[](int idx);
272 
273  /// Array subscript operator for host memory, const version.
274  inline const T &operator[](int idx) const;
275 
276  /// Direct access to the host memory as T* (implicit conversion).
277  /** When the type T is const-qualified, this method can be used only if the
278  host pointer is currently valid (the device pointer may be valid or
279  invalid).
280 
281  When the type T is not const-qualified, this method can be used only if
282  the host pointer is the only valid pointer.
283 
284  When the Memory is empty, this method can be used and it returns NULL. */
285  inline operator T*();
286 
287  /// Direct access to the host memory as const T* (implicit conversion).
288  /** This method can be used only if the host pointer is currently valid (the
289  device pointer may be valid or invalid).
290 
291  When the Memory is empty, this method can be used and it returns NULL. */
292  inline operator const T*() const;
293 
294  /// Direct access to the host memory via explicit typecast.
295  /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
296  In particular, this method cannot be used to cast away const-ness from
297  the base type T.
298 
299  When the type U is const-qualified, this method can be used only if the
300  host pointer is currently valid (the device pointer may be valid or
301  invalid).
302 
303  When the type U is not const-qualified, this method can be used only if
304  the host pointer is the only valid pointer.
305 
306  When the Memory is empty, this method can be used and it returns NULL. */
307  template <typename U>
308  inline explicit operator U*();
309 
310  /// Direct access to the host memory via explicit typecast, const version.
311  /** A pointer to type T must be reinterpret_cast-able to a pointer to type
312  const U.
313 
314  This method can be used only if the host pointer is currently valid (the
315  device pointer may be valid or invalid).
316 
317  When the Memory is empty, this method can be used and it returns NULL. */
318  template <typename U>
319  inline explicit operator const U*() const;
320 
321  /// Get read-write access to the memory with the given MemoryClass.
322  /** If only read or only write access is needed, then the methods
323  Read() or Write() should be used instead of this method.
324 
325  The parameter @a size must not exceed the Capacity(). */
326  inline T *ReadWrite(MemoryClass mc, int size);
327 
328  /// Get read-only access to the memory with the given MemoryClass.
329  /** The parameter @a size must not exceed the Capacity(). */
330  inline const T *Read(MemoryClass mc, int size) const;
331 
332  /// Get write-only access to the memory with the given MemoryClass.
333  /** The parameter @a size must not exceed the Capacity().
334 
335  The contents of the returned pointer is undefined, unless it was
336  validated by a previous call to Read() or ReadWrite() with
337  the same MemoryClass. */
338  inline T *Write(MemoryClass mc, int size);
339 
340  /// Copy the host/device pointer validity flags from @a other to @a *this.
341  /** This method synchronizes the pointer validity flags of two Memory objects
342  that use the same host/device pointers, or when @a *this is an alias
343  (sub-Memory) of @a other. Typically, this method should be called after
344  @a other is manipulated in a way that changes its pointer validity flags
345  (e.g. it was moved from device to host memory). */
346  inline void Sync(const Memory &other) const;
347 
348  /** @brief Update the alias Memory @a *this to match the memory location (all
349  valid locations) of its base Memory, @a base. */
350  /** This method is useful when alias Memory is moved and manipulated in a
351  different memory space. Such operations render the pointer validity flags
352  of the base incorrect. Calling this method will ensure that @a base is
353  up-to-date. Note that this is achieved by moving/copying @a *this (if
354  necessary), and not @a base. */
355  inline void SyncAlias(const Memory &base, int alias_size) const;
356 
357  /** @brief Return a MemoryType that is currently valid. If both the host and
358  the device pointers are currently valid, then the device memory type is
359  returned. */
360  inline MemoryType GetMemoryType() const;
361 
362  /// Copy @a size entries from @a src to @a *this.
363  /** The given @a size should not exceed the Capacity() of the source @a src
364  and the destination, @a *this. */
365  inline void CopyFrom(const Memory &src, int size);
366 
367  /// Copy @a size entries from the host pointer @a src to @a *this.
368  /** The given @a size should not exceed the Capacity() of @a *this. */
369  inline void CopyFromHost(const T *src, int size);
370 
371  /// Copy @a size entries from @a *this to @a dest.
372  /** The given @a size should not exceed the Capacity() of @a *this and the
373  destination, @a dest. */
374  inline void CopyTo(Memory &dest, int size) const
375  { dest.CopyFrom(*this, size); }
376 
377  /// Copy @a size entries from @a *this to the host pointer @a dest.
378  /** The given @a size should not exceed the Capacity() of @a *this. */
379  inline void CopyToHost(T *dest, int size) const;
380 };
381 
382 
383 /// The memory manager class
385 {
386 private:
387  template <typename T> friend class Memory;
388  // Used by the private static methods called by class Memory:
389  typedef Memory<int> Mem;
390 
391  /// Allow to detect if a global memory manager instance exists
392  static bool exists;
393 
394  // Methods used by class Memory
395 
396  // Allocate and register a new pointer. Return the host pointer.
397  // h_ptr must be already allocated using new T[] if mt is a pure device
398  // memory type, e.g. CUDA (mt will not be HOST).
399  static void *New_(void *h_ptr, std::size_t size, MemoryType mt,
400  unsigned &flags);
401 
402  // Register an external pointer of the given MemoryType. Return the host
403  // pointer.
404  static void *Register_(void *ptr, void *h_ptr, std::size_t capacity,
405  MemoryType mt, bool own, bool alias, unsigned &flags);
406 
407  // Register an alias. Return the host pointer. Note: base_h_ptr may be an
408  // alias.
409  static void Alias_(void *base_h_ptr, std::size_t offset, std::size_t size,
410  unsigned base_flags, unsigned &flags);
411 
412  // Un-register and free memory identified by its host pointer. Returns the
413  // memory type of the host pointer.
414  static MemoryType Delete_(void *h_ptr, unsigned flags);
415 
416  // Return a pointer to the memory identified by the host pointer h_ptr for
417  // access with the given MemoryClass.
418  static void *ReadWrite_(void *h_ptr, MemoryClass mc, std::size_t size,
419  unsigned &flags);
420 
421  static const void *Read_(void *h_ptr, MemoryClass mc, std::size_t size,
422  unsigned &flags);
423 
424  static void *Write_(void *h_ptr, MemoryClass mc, std::size_t size,
425  unsigned &flags);
426 
427  static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
428  size_t alias_size, unsigned base_flags,
429  unsigned &alias_flags);
430 
431  // Return the type the of the currently valid memory. If more than one types
432  // are valid, return a device type.
433  static MemoryType GetMemoryType_(void *h_ptr, unsigned flags);
434 
435  // Copy entries from valid memory type to valid memory type. Both dest_h_ptr
436  // and src_h_ptr are registered host pointers.
437  static void Copy_(void *dest_h_ptr, const void *src_h_ptr, std::size_t size,
438  unsigned src_flags, unsigned &dest_flags);
439 
440  // Copy entries from valid memory type to host memory, where dest_h_ptr is
441  // not a registered host pointer and src_h_ptr is a registered host pointer.
442  static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
443  std::size_t size, unsigned src_flags);
444 
445  // Copy entries from host memory to valid memory type, where dest_h_ptr is a
446  // registered host pointer and src_h_ptr is not a registered host pointer.
447  static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
448  std::size_t size, unsigned &dest_flags);
449 
450  /// Adds an address in the map
451  void *Insert(void *ptr, const std::size_t bytes);
452 
453  void InsertDevice(void *ptr, void *h_ptr, size_t bytes);
454 
455  /// Remove the address from the map, as well as all its aliases
456  void *Erase(void *ptr, bool free_dev_ptr = true);
457 
458  /// Return the corresponding device pointer of ptr, allocating and moving the
459  /// data if needed (used in OccaPtr)
460  void *GetDevicePtr(const void *ptr, size_t bytes, bool copy_data);
461 
462  void InsertAlias(const void *base_ptr, void *alias_ptr, bool base_is_alias);
463 
464  void EraseAlias(void *alias_ptr);
465 
466  void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
467 
468  /// Return true if the pointer has been registered
469  bool IsKnown(const void *ptr);
470 
471 public:
472  MemoryManager();
473  ~MemoryManager();
474 
475  void Destroy();
476 
477  /// Return true if a global memory manager instance exists
478  static bool Exists() { return exists; }
479 
480  /// Check if pointer has been registered in the memory manager
481  void RegisterCheck(void *ptr);
482 
483  /// Prints all pointers known by the memory manager
484  void PrintPtrs(void);
485 };
486 
487 
488 // Inline methods
489 
490 template <typename T>
491 inline void Memory<T>::New(int size, MemoryType mt)
492 {
493  if (mt == MemoryType::HOST)
494  {
495  New(size);
496  }
497  else
498  {
499  // Allocate the host pointer with new T[] if 'mt' is a pure device memory
500  // type, e.g. CUDA.
501  T *tmp = (mt == MemoryType::CUDA) ? new T[size] : NULL;
502  h_ptr = (T*)MemoryManager::New_(tmp, size*sizeof(T), mt, flags);
503  capacity = size;
504  }
505 }
506 
507 template <typename T>
508 inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
509 {
510  if (mt == MemoryType::HOST)
511  {
512  Wrap(ptr, size, own);
513  }
514  else
515  {
516  // Allocate the host pointer with new T[] if 'mt' is a pure device memory
517  // type, e.g. CUDA.
518  T *tmp = (mt == MemoryType::CUDA) ? new T[size] : NULL;
519  h_ptr = (T*)MemoryManager::Register_(ptr, tmp, size*sizeof(T), mt, own,
520  false, flags);
521  capacity = size;
522  }
523 }
524 
525 template <typename T>
526 inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
527 {
528  h_ptr = base.h_ptr + offset;
529  capacity = size;
530  if (!(base.flags & REGISTERED))
531  {
532  flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
533  }
534  else
535  {
536  MemoryManager::Alias_(base.h_ptr, offset*sizeof(T), size*sizeof(T),
537  base.flags, flags);
538  }
539 }
540 
541 template <typename T>
542 inline void Memory<T>::Delete()
543 {
544  if (!(flags & REGISTERED) ||
545  MemoryManager::Delete_(h_ptr, flags) == MemoryType::HOST)
546  {
547  if (flags & OWNS_HOST) { delete [] h_ptr; }
548  }
549 }
550 
551 template <typename T>
552 inline T &Memory<T>::operator[](int idx)
553 {
554  MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
555  "invalid host pointer access");
556  return h_ptr[idx];
557 }
558 
559 template <typename T>
560 inline const T &Memory<T>::operator[](int idx) const
561 {
562  MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
563  return h_ptr[idx];
564 }
565 
566 template <typename T>
568 {
569  MFEM_ASSERT(Empty() ||
570  ((flags & VALID_HOST) &&
571  (std::is_const<T>::value || !(flags & VALID_DEVICE))),
572  "invalid host pointer access");
573  return h_ptr;
574 }
575 
576 template <typename T>
577 inline Memory<T>::operator const T*() const
578 {
579  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
580  return h_ptr;
581 }
582 
583 template <typename T> template <typename U>
585 {
586  MFEM_ASSERT(Empty() ||
587  ((flags & VALID_HOST) &&
588  (std::is_const<U>::value || !(flags & VALID_DEVICE))),
589  "invalid host pointer access");
590  return reinterpret_cast<U*>(h_ptr);
591 }
592 
593 template <typename T> template <typename U>
594 inline Memory<T>::operator const U*() const
595 {
596  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
597  return reinterpret_cast<U*>(h_ptr);
598 }
599 
600 template <typename T>
601 inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
602 {
603  if (!(flags & REGISTERED))
604  {
605  if (mc == MemoryClass::HOST) { return h_ptr; }
606  MemoryManager::Register_(h_ptr, NULL, capacity*sizeof(T),
607  MemoryType::HOST, flags & OWNS_HOST,
608  flags & ALIAS, flags);
609  }
610  return (T*)MemoryManager::ReadWrite_(h_ptr, mc, size*sizeof(T), flags);
611 }
612 
613 template <typename T>
614 inline const T *Memory<T>::Read(MemoryClass mc, int size) const
615 {
616  if (!(flags & REGISTERED))
617  {
618  if (mc == MemoryClass::HOST) { return h_ptr; }
619  MemoryManager::Register_((void*)h_ptr, NULL, capacity*sizeof(T),
620  MemoryType::HOST, flags & OWNS_HOST,
621  flags & ALIAS, flags);
622  }
623  return (const T *)MemoryManager::Read_(
624  (void*)h_ptr, mc, size*sizeof(T), flags);
625 }
626 
627 template <typename T>
628 inline T *Memory<T>::Write(MemoryClass mc, int size)
629 {
630  if (!(flags & REGISTERED))
631  {
632  if (mc == MemoryClass::HOST) { return h_ptr; }
633  MemoryManager::Register_(h_ptr, NULL, capacity*sizeof(T),
634  MemoryType::HOST, flags & OWNS_HOST,
635  flags & ALIAS, flags);
636  }
637  return (T*)MemoryManager::Write_(h_ptr, mc, size*sizeof(T), flags);
638 }
639 
640 template <typename T>
641 inline void Memory<T>::Sync(const Memory &other) const
642 {
643  if (!(flags & REGISTERED) && (other.flags & REGISTERED))
644  {
645  MFEM_ASSERT(h_ptr == other.h_ptr &&
646  (flags & ALIAS) == (other.flags & ALIAS),
647  "invalid input");
648  flags = (flags | REGISTERED) & ~(OWNS_DEVICE | OWNS_INTERNAL);
649  }
650  flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
651  (other.flags & (VALID_HOST | VALID_DEVICE));
652 }
653 
654 template <typename T>
655 inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
656 {
657  // Assuming that if *this is registered then base is also registered.
658  MFEM_ASSERT(!(flags & REGISTERED) || (base.flags & REGISTERED),
659  "invalid base state");
660  if (!(base.flags & REGISTERED)) { return; }
661  MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
662  base.flags, flags);
663 }
664 
665 template <typename T>
667 {
668  if (!(flags & REGISTERED)) { return MemoryType::HOST; }
669  return MemoryManager::GetMemoryType_(h_ptr, flags);
670 }
671 
672 template <typename T>
673 inline void Memory<T>::CopyFrom(const Memory &src, int size)
674 {
675  if (!(flags & REGISTERED) && !(src.flags & REGISTERED))
676  {
677  if (h_ptr != src.h_ptr && size != 0)
678  {
679  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
680  "data overlaps!");
681  std::memcpy(h_ptr, src, size*sizeof(T));
682  }
683  // *this is not registered, so (flags & VALID_HOST) must be true
684  }
685  else
686  {
687  MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
688  }
689 }
690 
691 template <typename T>
692 inline void Memory<T>::CopyFromHost(const T *src, int size)
693 {
694  if (!(flags & REGISTERED))
695  {
696  if (h_ptr != src && size != 0)
697  {
698  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
699  "data overlaps!");
700  std::memcpy(h_ptr, src, size*sizeof(T));
701  }
702  // *this is not registered, so (flags & VALID_HOST) must be true
703  }
704  else
705  {
706  MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
707  }
708 }
709 
710 template <typename T>
711 inline void Memory<T>::CopyToHost(T *dest, int size) const
712 {
713  if (!(flags & REGISTERED))
714  {
715  if (h_ptr != dest && size != 0)
716  {
717  MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
718  "data overlaps!");
719  std::memcpy(dest, h_ptr, size*sizeof(T));
720  }
721  }
722  else
723  {
724  MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
725  }
726 }
727 
728 
729 /** @brief Print the state of a Memory object based on its internal flags.
730  Useful in a debugger. */
731 extern void MemoryPrintFlags(unsigned flags);
732 
733 
734 /// The (single) global memory manager object
735 extern MemoryManager mm;
736 
737 } // namespace mfem
738 
739 #endif // MFEM_MEM_MANAGER_HPP
Host memory aligned at 64 bytes (not supported yet)
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
Definition: mem_manager.hpp:50
~Memory()=default
Destructor: default.
friend void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger.
The device pointer will be deleted by Delete()
void Delete()
Delete the owned pointers. The Memory is not reset by this method.
Memory(T *ptr, int size, MemoryType mt, bool own)
Wrap an externally allocated pointer, ptr, of the given MemoryType.
void SyncAlias(const Memory &base, int alias_size) const
Update the alias Memory *this to match the memory location (all valid locations) of its base Memory...
void SetDevicePtrOwner(bool own) const
Set/clear the ownership flag for the device pointer. Ownership indicates whether the pointer will be ...
Memory types: { CUDA, CUDA_UVM }.
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
Internal device flag, see e.g. Vector::UseDevice()
Memory(int size)
Allocate host memory for size entries.
void UseDevice(bool use_dev) const
Set the internal device flag.
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
int Capacity() const
Return the size of the allocated memory.
Memory(int size, MemoryType mt)
Allocate memory for size entries with the given MemoryType mt.
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
unsigned flags
Host pointer is valid.
Host memory aligned at 32 bytes (not supported yet)
void Wrap(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with type MemoryType::HOST.
void CopyFromHost(const T *src, int size)
Copy size entries from the host pointer src to *this.
void CopyTo(Memory &dest, int size) const
Copy size entries from *this to dest.
The memory manager class.
Memory(const Memory &base, int offset, int size)
Alias constructor. Create a Memory object that points inside the Memory object base.
bool OwnsHostPtr() const
Return true if the host pointer is owned. Ownership indicates whether the pointer will be deleted by ...
h_ptr is registered with the MemoryManager
The host pointer will be deleted by Delete()
Memory & operator=(const Memory &orig)=default
Copy-assignment operator: default.
Device pointer is valid.
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
Ownership flag for internal Memory data.
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:27
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
void RegisterCheck(void *ptr)
Check if pointer has been registered in the memory manager.
void Sync(const Memory &other) const
Copy the host/device pointer validity flags from other to *this.
cudaMalloc, cudaFree
void MakeAlias(const Memory &base, int offset, int size)
Create a memory object that points inside the memory object base.
MemoryManager mm
The (single) global memory manager object.
T * h_ptr
Pointer to host memory. Not owned.
Host memory; using new[] and delete[].
void PrintPtrs(void)
Prints all pointers known by the memory manager.
void New(int size)
Allocate host memory for size entries with type MemoryType::HOST.
bool Empty() const
Return true if the Memory object is empty, see Reset().
cudaMallocManaged, cudaFree (not supported yet)
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
Definition: mem_manager.cpp:23
void CopyToHost(T *dest, int size) const
Copy size entries from *this to the host pointer dest.
Class used by MFEM to store pointers to host and/or device memory.
Memory()
Default constructor: no initialization.
bool OwnsDevicePtr() const
Return true if the device pointer is owned. Ownership indicates whether the pointer will be deleted b...
bool UseDevice() const
Read the internal device flag.
Memory(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with type MemoryType::HOST.
T & operator[](int idx)
Array subscript operator for host memory.
static bool Exists()
Return true if a global memory manager instance exists.
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
Return a suitable MemoryClass from a pair of MemoryClasses.
Definition: mem_manager.cpp:36
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
MemoryClass
Memory classes identify subsets of memory types.
Definition: mem_manager.hpp:40
void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger.