MFEM  v4.5.2
Finite element discretization library
mem_manager.hpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
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 #include <cstddef> // std::max_align_t
20 #ifdef MFEM_USE_MPI
21 #include <HYPRE_config.h> // HYPRE_USING_GPU
22 #endif
23 
24 namespace mfem
25 {
26 
27 // Implementation of MFEM's lightweight device/host memory manager designed to
28 // work seamlessly with the OCCA, RAJA, and other kernels supported by MFEM.
29 
30 /// Memory types supported by MFEM.
31 enum class MemoryType
32 {
33  HOST, ///< Host memory; using new[] and delete[]
34  HOST_32, ///< Host memory; aligned at 32 bytes
35  HOST_64, ///< Host memory; aligned at 64 bytes
36  HOST_DEBUG, ///< Host memory; allocated from a "host-debug" pool
37  HOST_UMPIRE, /**< Host memory; using an Umpire allocator which can be set
38  with MemoryManager::SetUmpireHostAllocatorName */
39  HOST_PINNED, ///< Host memory: pinned (page-locked)
40  MANAGED, /**< Managed memory; using CUDA or HIP *MallocManaged
41  and *Free */
42  DEVICE, ///< Device memory; using CUDA or HIP *Malloc and *Free
43  DEVICE_DEBUG, /**< Pseudo-device memory; allocated on host from a
44  "device-debug" pool */
45  DEVICE_UMPIRE, /**< Device memory; using an Umpire allocator which can be
46  set with MemoryManager::SetUmpireDeviceAllocatorName */
47  DEVICE_UMPIRE_2, /**< Device memory; using a second Umpire allocator settable
48  with MemoryManager::SetUmpireDevice2AllocatorName */
49  SIZE, ///< Number of host and device memory types
50 
51  PRESERVE, /**< Pseudo-MemoryType used as default value for MemoryType
52  parameters to request preservation of existing
53  MemoryType, e.g. in copy constructors. */
54  DEFAULT /**< Pseudo-MemoryType used as default value for MemoryType
55  parameters to request the use of the default host or
56  device MemoryType. */
57 };
58 
59 /// Static casts to 'int' and sizes of some useful memory types.
60 constexpr int MemoryTypeSize = static_cast<int>(MemoryType::SIZE);
61 constexpr int HostMemoryType = static_cast<int>(MemoryType::HOST);
62 constexpr int HostMemoryTypeSize = static_cast<int>(MemoryType::DEVICE);
63 constexpr int DeviceMemoryType = static_cast<int>(MemoryType::MANAGED);
65 
66 /// Memory type names, used during Device:: configuration.
67 extern MFEM_EXPORT const char *MemoryTypeName[MemoryTypeSize];
68 
69 /// Memory classes identify sets of memory types.
70 /** This type is used by kernels that can work with multiple MemoryType%s.
71  * For example, kernels that can use DEVICE or MANAGED memory types should
72  * use MemoryClass::DEVICE for their inputs. */
73 enum class MemoryClass
74 {
75  HOST, /**< Memory types: { HOST, HOST_32, HOST_64, HOST_DEBUG,
76  HOST_UMPIRE, HOST_PINNED, MANAGED } */
77  HOST_32, ///< Memory types: { HOST_32, HOST_64, HOST_DEBUG }
78  HOST_64, ///< Memory types: { HOST_64, HOST_DEBUG }
79  DEVICE, /**< Memory types: { DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE,
80  DEVICE_UMPIRE_2, MANAGED } */
81  MANAGED ///< Memory types: { MANAGED }
82 };
83 
84 /// Return true if the given memory type is in MemoryClass::HOST.
85 inline bool IsHostMemory(MemoryType mt) { return mt <= MemoryType::MANAGED; }
86 
87 /// Return true if the given memory type is in MemoryClass::DEVICE
88 inline bool IsDeviceMemory(MemoryType mt)
89 {
90  return mt >= MemoryType::MANAGED && mt < MemoryType::SIZE;
91 }
92 
93 /// Return a suitable MemoryType for a given MemoryClass.
95 
96 /// Return true iff the MemoryType @a mt is contained in the MemoryClass @a mc.
98 
99 /// Return a suitable MemoryClass from a pair of MemoryClass%es.
100 /** Note: this operation is commutative, i.e. a*b = b*a, associative, i.e.
101  (a*b)*c = a*(b*c), and has an identity element: MemoryClass::HOST.
102 
103  Currently, the operation is defined as a*b := max(a,b) where the max
104  operation is based on the enumeration ordering:
105 
106  HOST < HOST_32 < HOST_64 < DEVICE < MANAGED. */
108 
109 /// Class used by MFEM to store pointers to host and/or device memory.
110 /** The template class parameter, T, must be a plain-old-data (POD) type.
111 
112  In many respects this class behaves like a pointer:
113  - When destroyed, a Memory object does NOT automatically delete any
114  allocated memory.
115  - Only the method Delete() will deallocate a Memory object.
116  - Other methods that modify the object (e.g. New(), Wrap(), etc) will
117  simply overwrite the old contents.
118  In other aspects this class differs from a pointer:
119  - Pointer arithmetic is not supported, MakeAlias() should be used instead.
120  - Const Memory object does not allow modification of the content
121  (unlike e.g. a const pointer).
122  - Move constructor and assignment will transfer ownership flags, and
123  Reset() the moved Memory object.
124  - Copy constructor and assignment copy flags. This may result in two Memory
125  objects owning the data which is an invalid state. This invalid state MUST
126  be resolved by users manually using SetHostPtrOwner(),
127  SetDevicePtrOwner(), or ClearOwnerFlags(). It is also possible to call
128  Delete() on only one of the two Memory objects, however this is
129  discouraged because it bypasses the internal ownership flags.
130  - When moving or copying (between host and device) alias Memory objects
131  and/or their base Memory objects, the consistency of memory flags have
132  to be manually taken care of using either Sync() or SyncAlias(). Failure
133  to do so will result in silent misuse of unsynchronized data.
134 
135  A Memory object stores up to two different pointers: one host pointer (with
136  MemoryType from MemoryClass::HOST) and one device pointer (currently one of
137  MemoryType: DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE or MANAGED).
138 
139  A Memory object can hold (wrap) an externally allocated pointer with any
140  given MemoryType.
141 
142  Access to the content of the Memory object can be requested with any given
143  MemoryClass through the methods ReadWrite(), Read(), and Write().
144  Requesting such access may result in additional (internally handled)
145  memory allocation and/or memory copy.
146  - When ReadWrite() is called, the returned pointer becomes the only
147  valid pointer.
148  - When Read() is called, the returned pointer becomes valid, however
149  the other pointer (host or device) may remain valid as well.
150  - When Write() is called, the returned pointer becomes the only valid
151  pointer, however, unlike ReadWrite(), no memory copy will be performed.
152 
153  The host memory (pointer from MemoryClass::HOST) can be accessed through the
154  inline methods: `operator[]()`, `operator*()`, the implicit conversion
155  functions `operator T*()`, `operator const T*()`, and the explicit
156  conversion template functions `operator U*()`, `operator const U*()` (with
157  any suitable type U). In certain cases, using these methods may have
158  undefined behavior, e.g. if the host pointer is not currently valid. */
159 template <typename T>
160 class Memory
161 {
162 protected:
163  friend class MemoryManager;
164  friend void MemoryPrintFlags(unsigned flags);
165 
166  enum FlagMask: unsigned
167  {
168  // Workaround for use with headers that define REGISTERED as a macro,
169  // e.g. nb30.h (which is included by Windows.h):
170 #ifndef REGISTERED
171  REGISTERED = 1 << 0, /**< The host pointer is registered with the
172  MemoryManager */
173 #endif
174  // Use the following identifier if REGISTERED is defined as a macro,
175  // e.g. nb30.h (which is included by Windows.h):
176  Registered = 1 << 0, /**< The host pointer is registered with the
177  MemoryManager */
178  OWNS_HOST = 1 << 1, ///< The host pointer will be deleted by Delete()
179  OWNS_DEVICE = 1 << 2, /**< The device pointer will be deleted by
180  Delete() */
181  OWNS_INTERNAL = 1 << 3, ///< Ownership flag for internal Memory data
182  VALID_HOST = 1 << 4, ///< Host pointer is valid
183  VALID_DEVICE = 1 << 5, ///< %Device pointer is valid
184  USE_DEVICE = 1 << 6, /**< Internal device flag, see e.g.
185  Vector::UseDevice() */
186  ALIAS = 1 << 7 ///< Pointer is an alias
187  };
188 
189  /// Pointer to host memory. Not owned.
190  /** The type of the pointer is given by the field #h_mt; it can be any type
191  from MemoryClass::HOST. */
192  T *h_ptr;
193  int capacity; ///< Size of the allocated memory
194  MemoryType h_mt; ///< Host memory type
195  mutable unsigned flags; ///< Bit flags defined from the #FlagMask enum
196  // 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
197  // Copy{From,To}, {ReadWrite,Read,Write}.
198 
199 public:
200  /** Default constructor, sets the host pointer to nullptr and the metadata to
201  meaningful default values. */
202  Memory() { Reset(); }
203 
204  /// Copy constructor: default.
205  Memory(const Memory &orig) = default;
206 
207  /** Move constructor. Sets the pointers and associated ownership of validity
208  flags of @a *this to those of @a other. Resets @a other. */
209  Memory(Memory &&orig)
210  {
211  *this = orig;
212  orig.Reset();
213  }
214 
215  /// Copy-assignment operator: default.
216  Memory &operator=(const Memory &orig) = default;
217 
218  /** Move assignment operator. Sets the pointers and associated ownership of
219  validity flags of @a *this to those of @a other. Resets @a other. */
221  {
222  // Guard self-assignment:
223  if (this == &orig) { return *this; }
224  *this = orig;
225  orig.Reset();
226  return *this;
227  }
228 
229  /// Allocate host memory for @a size entries.
230  /** The allocation uses the current host memory type returned by
231  MemoryManager::GetHostMemoryType(). */
232  explicit Memory(int size) { New(size); }
233 
234  /** @brief Allocate memory for @a size entries with the given MemoryType
235  @a mt. */
236  /** The newly allocated memory is not initialized, however the given
237  MemoryType is still set as valid. */
238  Memory(int size, MemoryType mt) { New(size, mt); }
239 
240  /** @brief Allocate memory for @a size entries with the given host MemoryType
241  @a h_mt and device MemoryType @a d_mt. */
242  /** The newly allocated memory is not initialized. The host pointer is set as
243  valid. */
244  Memory(int size, MemoryType h_mt, MemoryType d_mt) { New(size, h_mt, d_mt); }
245 
246  /** @brief Wrap an externally allocated host pointer, @a ptr with the current
247  host memory type returned by MemoryManager::GetHostMemoryType(). */
248  /** The parameter @a own determines whether @a ptr will be deleted when the
249  method Delete() is called. */
250  explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
251 
252  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
253  /** The new memory object will have the given MemoryType set as valid.
254 
255  The given @a ptr must be allocated appropriately for the given
256  MemoryType.
257 
258  The parameter @a own determines whether @a ptr will be deleted when the
259  method Delete() is called. */
260  Memory(T *ptr, int size, MemoryType mt, bool own)
261  { Wrap(ptr, size, mt, own); }
262 
263  /** @brief Alias constructor. Create a Memory object that points inside the
264  Memory object @a base. */
265  /** The new Memory object uses the same MemoryType(s) as @a base. */
266  Memory(const Memory &base, int offset, int size)
267  { MakeAlias(base, offset, size); }
268 
269  /// Destructor: default.
270  /** @note The destructor will NOT delete the current memory. */
271  ~Memory() = default;
272 
273  /** @brief Return true if the host pointer is owned. Ownership indicates
274  whether the pointer will be deleted by the method Delete(). */
275  bool OwnsHostPtr() const { return flags & OWNS_HOST; }
276 
277  /** @brief Set/clear the ownership flag for the host pointer. Ownership
278  indicates whether the pointer will be deleted by the method Delete(). */
279  void SetHostPtrOwner(bool own) const
280  { flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
281 
282  /** @brief Return true if the device pointer is owned. Ownership indicates
283  whether the pointer will be deleted by the method Delete(). */
284  bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
285 
286  /** @brief Set/clear the ownership flag for the device pointer. Ownership
287  indicates whether the pointer will be deleted by the method Delete(). */
288  void SetDevicePtrOwner(bool own) const
289  { flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
290 
291  /** @brief Clear the ownership flags for the host and device pointers, as
292  well as any internal data allocated by the Memory object. */
293  void ClearOwnerFlags() const
295 
296  /// Read the internal device flag.
297  bool UseDevice() const { return flags & USE_DEVICE; }
298 
299  /// Set the internal device flag.
300  void UseDevice(bool use_dev) const
301  { flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
302 
303  /// Return the size of the allocated memory.
304  int Capacity() const { return capacity; }
305 
306  /// Reset the memory to be empty, ensuring that Delete() will be a no-op.
307  /** This is the Memory class equivalent to setting a pointer to NULL, see
308  Empty().
309 
310  @note The current memory is NOT deleted by this method. */
311  void Reset();
312 
313  /// Reset the memory and set the host memory type.
314  void Reset(MemoryType host_mt);
315 
316  /// Return true if the Memory object is empty, see Reset().
317  /** Default-constructed objects are uninitialized, so they are not guaranteed
318  to be empty. */
319  bool Empty() const { return h_ptr == NULL; }
320 
321  /** @brief Allocate host memory for @a size entries with the current host
322  memory type returned by MemoryManager::GetHostMemoryType(). */
323  /** @note The current memory is NOT deleted by this method. */
324  inline void New(int size);
325 
326  /// Allocate memory for @a size entries with the given MemoryType.
327  /** The newly allocated memory is not initialized, however the given
328  MemoryType is still set as valid.
329 
330  When @a mt is a host type, the device MemoryType will be set later, if
331  requested, using the dual type of @a mt, see
332  MemoryManager::GetDualMemoryType().
333 
334  When @a mt is a device type, the host MemoryType will be set immediately
335  to be the dual of @a mt, see MemoryManager::GetDualMemoryType().
336 
337  @note The current memory is NOT deleted by this method. */
338  inline void New(int size, MemoryType mt);
339 
340  /** @brief Allocate memory for @a size entries with the given host MemoryType
341  @a h_mt and device MemoryType @a d_mt. */
342  /** The newly allocated memory is not initialized. The host pointer is set as
343  valid.
344 
345  @note The current memory is NOT deleted by this method. */
346  inline void New(int size, MemoryType h_mt, MemoryType d_mt);
347 
348  /** @brief Wrap an externally allocated host pointer, @a ptr with the current
349  host memory type returned by MemoryManager::GetHostMemoryType(). */
350  /** The parameter @a own determines whether @a ptr will be deleted when the
351  method Delete() is called.
352 
353  @note The current memory is NOT deleted by this method. */
354  inline void Wrap(T *ptr, int size, bool own);
355 
356  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
357  /** The new memory object will have the given MemoryType set as valid.
358 
359  The given @a ptr must be allocated appropriately for the given
360  MemoryType.
361 
362  The parameter @a own determines whether @a ptr will be deleted when the
363  method Delete() is called.
364 
365  @note The current memory is NOT deleted by this method. */
366  inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
367 
368  /** Wrap an externally pair of allocated pointers, @a h_ptr and @a d_ptr,
369  of the given host MemoryType @a h_mt. */
370  /** The new memory object will have the device MemoryType set as valid.
371 
372  The given @a h_ptr and @a d_ptr must be allocated appropriately for the
373  given host MemoryType and its dual device MemoryType as defined by
374  MemoryManager::GetDualMemoryType().
375 
376  The parameter @a own determines whether both @a h_ptr and @a d_ptr will
377  be deleted when the method Delete() is called.
378 
379  @note Ownership can also be controlled by using the following methods:
380  - ClearOwnerFlags,
381  - SetHostPtrOwner,
382  - SetDevicePtrOwner.
383 
384  @note The current memory is NOT deleted by this method. */
385  inline void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own);
386 
387  /// Create a memory object that points inside the memory object @a base.
388  /** The new Memory object uses the same MemoryType(s) as @a base.
389 
390  @note The current memory is NOT deleted by this method. */
391  inline void MakeAlias(const Memory &base, int offset, int size);
392 
393  /// Set the device MemoryType to be used by the Memory object.
394  /** If the specified @a d_mt is not a device MemoryType, i.e. not one of the
395  types in MemoryClass::DEVICE, then this method will return immediately.
396 
397  If the device MemoryType has been previously set to a different type and
398  the actual device memory has been allocated, this method will trigger an
399  error. This method will not perform the actual device memory allocation,
400  however, the allocation may already exist if the MemoryType is the same
401  as the current one.
402 
403  If the Memory is an alias Memory, the device MemoryType of its base will
404  be updated as described above. */
405  inline void SetDeviceMemoryType(MemoryType d_mt);
406 
407  /** @brief Delete the owned pointers and reset the Memory object. */
408  inline void Delete();
409 
410  /** @brief Delete the device pointer, if owned. If @a copy_to_host is true
411  and the data is valid only on device, move it to host before deleting.
412  Invalidates the device memory. */
413  inline void DeleteDevice(bool copy_to_host = true);
414 
415  /// Array subscript operator for host memory.
416  inline T &operator[](int idx);
417 
418  /// Array subscript operator for host memory, const version.
419  inline const T &operator[](int idx) const;
420 
421  /// Direct access to the host memory as T* (implicit conversion).
422  /** When the type T is const-qualified, this method can be used only if the
423  host pointer is currently valid (the device pointer may be valid or
424  invalid).
425 
426  When the type T is not const-qualified, this method can be used only if
427  the host pointer is the only valid pointer.
428 
429  When the Memory is empty, this method can be used and it returns NULL. */
430  inline operator T*();
431 
432  /// Direct access to the host memory as const T* (implicit conversion).
433  /** This method can be used only if the host pointer is currently valid (the
434  device pointer may be valid or invalid).
435 
436  When the Memory is empty, this method can be used and it returns NULL. */
437  inline operator const T*() const;
438 
439  /// Direct access to the host memory via explicit typecast.
440  /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
441  In particular, this method cannot be used to cast away const-ness from
442  the base type T.
443 
444  When the type U is const-qualified, this method can be used only if the
445  host pointer is currently valid (the device pointer may be valid or
446  invalid).
447 
448  When the type U is not const-qualified, this method can be used only if
449  the host pointer is the only valid pointer.
450 
451  When the Memory is empty, this method can be used and it returns NULL. */
452  template <typename U>
453  inline explicit operator U*();
454 
455  /// Direct access to the host memory via explicit typecast, const version.
456  /** A pointer to type T must be reinterpret_cast-able to a pointer to type
457  const U.
458 
459  This method can be used only if the host pointer is currently valid (the
460  device pointer may be valid or invalid).
461 
462  When the Memory is empty, this method can be used and it returns NULL. */
463  template <typename U>
464  inline explicit operator const U*() const;
465 
466  /// Get read-write access to the memory with the given MemoryClass.
467  /** If only read or only write access is needed, then the methods
468  Read() or Write() should be used instead of this method.
469 
470  The parameter @a size must not exceed the Capacity(). */
471  inline T *ReadWrite(MemoryClass mc, int size);
472 
473  /// Get read-only access to the memory with the given MemoryClass.
474  /** The parameter @a size must not exceed the Capacity(). */
475  inline const T *Read(MemoryClass mc, int size) const;
476 
477  /// Get write-only access to the memory with the given MemoryClass.
478  /** The parameter @a size must not exceed the Capacity().
479 
480  The contents of the returned pointer is undefined, unless it was
481  validated by a previous call to Read() or ReadWrite() with
482  the same MemoryClass. */
483  inline T *Write(MemoryClass mc, int size);
484 
485  /// Copy the host/device pointer validity flags from @a other to @a *this.
486  /** This method synchronizes the pointer validity flags of two Memory objects
487  that use the same host/device pointers, or when @a *this is an alias
488  (sub-Memory) of @a other. Typically, this method should be called after
489  @a other is manipulated in a way that changes its pointer validity flags
490  (e.g. it was moved from device to host memory). */
491  inline void Sync(const Memory &other) const;
492 
493  /** @brief Update the alias Memory @a *this to match the memory location (all
494  valid locations) of its base Memory, @a base. */
495  /** This method is useful when alias Memory is moved and manipulated in a
496  different memory space. Such operations render the pointer validity flags
497  of the base incorrect. Calling this method will ensure that @a base is
498  up-to-date. Note that this is achieved by moving/copying @a *this (if
499  necessary), and not @a base. */
500  inline void SyncAlias(const Memory &base, int alias_size) const;
501 
502  /** @brief Return a MemoryType that is currently valid. If both the host and
503  the device pointers are currently valid, then the device memory type is
504  returned. */
505  inline MemoryType GetMemoryType() const;
506 
507  /// Return the host MemoryType of the Memory object.
508  inline MemoryType GetHostMemoryType() const { return h_mt; }
509 
510  /** @brief Return the device MemoryType of the Memory object. If the device
511  MemoryType is not set, return MemoryType::DEFAULT. */
512  inline MemoryType GetDeviceMemoryType() const;
513 
514  /** @brief Return true if host pointer is valid */
515  inline bool HostIsValid() const;
516 
517  /** @brief Return true if device pointer is valid */
518  inline bool DeviceIsValid() const;
519 
520  /// Copy @a size entries from @a src to @a *this.
521  /** The given @a size should not exceed the Capacity() of the source @a src
522  and the destination, @a *this. */
523  inline void CopyFrom(const Memory &src, int size);
524 
525  /// Copy @a size entries from the host pointer @a src to @a *this.
526  /** The given @a size should not exceed the Capacity() of @a *this. */
527  inline void CopyFromHost(const T *src, int size);
528 
529  /// Copy @a size entries from @a *this to @a dest.
530  /** The given @a size should not exceed the Capacity() of @a *this and the
531  destination, @a dest. */
532  inline void CopyTo(Memory &dest, int size) const;
533 
534  /// Copy @a size entries from @a *this to the host pointer @a dest.
535  /** The given @a size should not exceed the Capacity() of @a *this. */
536  inline void CopyToHost(T *dest, int size) const;
537 
538  /// Print the internal flags.
539  /** This method can be useful for debugging. It is explicitly instantiated
540  for Memory<T> with T = int and T = double. */
541  inline void PrintFlags() const;
542 
543  /// If both the host and the device data are valid, compare their contents.
544  /** This method can be useful for debugging. It is explicitly instantiated
545  for Memory<T> with T = int and T = double. */
546  inline int CompareHostAndDevice(int size) const;
547 
548 private:
549  // GCC 4.8 workaround: max_align_t is not in std.
550  static constexpr std::size_t def_align_bytes_()
551  {
552  using namespace std;
553  return alignof(max_align_t);
554  }
555  static constexpr std::size_t def_align_bytes = def_align_bytes_();
556  static constexpr std::size_t new_align_bytes =
557  alignof(T) > def_align_bytes ? alignof(T) : def_align_bytes;
558 
559  template <std::size_t align_bytes, bool dummy = true> struct Alloc
560  {
561 #if __cplusplus < 201703L
562  static inline T *New(std::size_t)
563  {
564  // Generate an error in debug mode
565  MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
566  return nullptr;
567  }
568 #else
569  static inline T *New(std::size_t size) { return new T[size]; }
570 #endif
571  };
572 
573 #if __cplusplus < 201703L
574  template<bool dummy> struct Alloc<def_align_bytes,dummy>
575  {
576  static inline T *New(std::size_t size) { return new T[size]; }
577  };
578 #endif
579 
580  // Shortcut for Alloc<new_align_bytes>::New(size)
581  static inline T *NewHOST(std::size_t size)
582  {
583  return Alloc<new_align_bytes>::New(size);
584  }
585 };
586 
587 
588 /** The MFEM memory manager class. Host-side pointers are inserted into this
589  manager which keeps track of the associated device pointer, and where the
590  data currently resides. */
591 class MFEM_EXPORT MemoryManager
592 {
593 private:
594 
595  typedef MemoryType MemType;
596  typedef Memory<int> Mem;
597 
598  template <typename T> friend class Memory;
599 
600  /// Host memory type set during the Setup.
601  static MemoryType host_mem_type;
602 
603  /// Device memory type set during the Setup.
604  static MemoryType device_mem_type;
605 
606  /// Allow to detect if a global memory manager instance exists.
607  static bool exists;
608 
609  /// Return true if the global memory manager instance exists.
610  static bool Exists() { return exists; }
611 
612  /// Array defining the dual MemoryType for each MemoryType
613  /** The dual of a host MemoryType is a device MemoryType and vice versa: the
614  dual of a device MemoryType is a host MemoryType. */
615  static MemoryType dual_map[MemoryTypeSize];
616 
617  /// Update the dual memory type of @a mt to be @a dual_mt.
618  static void UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt);
619 
620  /// True if Configure() was called.
621  static bool configured;
622 
623  /// Host and device allocator names for Umpire.
624 #ifdef MFEM_USE_UMPIRE
625  static const char * h_umpire_name;
626  static const char * d_umpire_name;
627  static const char * d_umpire_2_name;
628 #endif
629 
630 private: // Static methods used by the Memory<T> class
631 
632  /// Allocate and register a new pointer. Return the host pointer.
633  /// h_tmp must be already allocated using new T[] if mt is a pure device
634  /// memory type, e.g. CUDA (mt will not be HOST).
635  static void *New_(void *h_tmp, size_t bytes, MemoryType mt, unsigned &flags);
636 
637  static void *New_(void *h_tmp, size_t bytes, MemoryType h_mt,
638  MemoryType d_mt, unsigned valid_flags, unsigned &flags);
639 
640  /// Register an external pointer of the given MemoryType.
641  /// Return the host pointer.
642  static void *Register_(void *ptr, void *h_ptr, size_t bytes, MemoryType mt,
643  bool own, bool alias, unsigned &flags);
644 
645  /// Register a pair of external host and device pointers
646  static void Register2_(void *h_ptr, void *d_ptr, size_t bytes,
647  MemoryType h_mt, MemoryType d_mt,
648  bool own, bool alias, unsigned &flags);
649 
650  /// Register an alias. Note: base_h_ptr may be an alias.
651  static void Alias_(void *base_h_ptr, size_t offset, size_t bytes,
652  unsigned base_flags, unsigned &flags);
653 
654  static void SetDeviceMemoryType_(void *h_ptr, unsigned flags,
655  MemoryType d_mt);
656 
657  /// Un-register and free memory identified by its host pointer.
658  static void Delete_(void *h_ptr, MemoryType mt, unsigned flags);
659 
660  /// Free device memory identified by its host pointer
661  static void DeleteDevice_(void *h_ptr, unsigned & flags);
662 
663  /// Check if the memory types given the memory class are valid
664  static bool MemoryClassCheck_(MemoryClass mc, void *h_ptr,
665  MemoryType h_mt, size_t bytes, unsigned flags);
666 
667  /// Return a pointer to the memory identified by the host pointer h_ptr for
668  /// access with the given MemoryClass.
669  static void *ReadWrite_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
670  size_t bytes, unsigned &flags);
671 
672  static const void *Read_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
673  size_t bytes, unsigned &flags);
674 
675  static void *Write_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
676  size_t bytes, unsigned &flags);
677 
678  static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
679  size_t alias_bytes, unsigned base_flags,
680  unsigned &alias_flags);
681 
682  /// Return the type the of the currently valid memory.
683  /// If more than one types are valid, return a device type.
684  static MemoryType GetDeviceMemoryType_(void *h_ptr, bool alias);
685 
686  /// Return the type the of the host memory.
687  static MemoryType GetHostMemoryType_(void *h_ptr);
688 
689  /// Verify that h_mt and h_ptr's h_mt (memory or alias) are equal.
690  static void CheckHostMemoryType_(MemoryType h_mt, void *h_ptr, bool alias);
691 
692  /// Copy entries from valid memory type to valid memory type.
693  /// Both dest_h_ptr and src_h_ptr are registered host pointers.
694  static void Copy_(void *dest_h_ptr, const void *src_h_ptr, size_t bytes,
695  unsigned src_flags, unsigned &dest_flags);
696 
697  /// Copy entries from valid memory type to host memory, where dest_h_ptr is
698  /// not a registered host pointer and src_h_ptr is a registered host pointer.
699  static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
700  size_t bytes, unsigned src_flags);
701 
702  /// Copy entries from host memory to valid memory type, where dest_h_ptr is a
703  /// registered host pointer and src_h_ptr is not a registered host pointer.
704  static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
705  size_t bytes, unsigned &dest_flags);
706 
707  /// Check if the host pointer has been registered in the memory manager.
708  static bool IsKnown_(const void *h_ptr);
709 
710  /** @brief Check if the host pointer has been registered as an alias in the
711  memory manager. */
712  static bool IsAlias_(const void *h_ptr);
713 
714  /// Compare the contents of the host and the device memory.
715  static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
716 
717 private:
718 
719  /// Insert a host address @a h_ptr and size *a bytes in the memory map to be
720  /// managed.
721  void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
722 
723  /// Insert a device and the host addresses in the memory map
724  void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
725  MemoryType h_mt, MemoryType d_mt);
726 
727  /// Insert an alias in the alias map
728  void InsertAlias(const void *base_ptr, void *alias_ptr,
729  const size_t bytes, const bool base_is_alias);
730 
731  /// Erase an address from the memory map, as well as all its aliases
732  void Erase(void *h_ptr, bool free_dev_ptr = true);
733 
734  /// Erase device memory for a given host address
735  void EraseDevice(void *h_ptr);
736 
737  /// Erase an alias from the aliases map
738  void EraseAlias(void *alias_ptr);
739 
740  /// Return the corresponding device pointer of h_ptr,
741  /// allocating and moving the data if needed
742  void *GetDevicePtr(const void *h_ptr, size_t bytes, bool copy_data);
743 
744  /// Return the corresponding device pointer of alias_ptr,
745  /// allocating and moving the data if needed
746  void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
747 
748  /// Return the corresponding host pointer of d_ptr,
749  /// allocating and moving the data if needed
750  void *GetHostPtr(const void *d_ptr, size_t bytes, bool copy_data);
751 
752  /// Return the corresponding host pointer of alias_ptr,
753  /// allocating and moving the data if needed
754  void *GetAliasHostPtr(const void *alias_ptr, size_t bytes, bool copy_data);
755 
756 public:
757  MemoryManager();
758  ~MemoryManager();
759 
760  /// Initialize the memory manager.
761  void Init();
762 
763  /// Return the dual MemoryType of the given one, @a mt.
764  /** The default dual memory types are:
765 
766  memory type | dual type
767  --------------- | ---------
768  HOST | DEVICE
769  HOST_32 | DEVICE
770  HOST_64 | DEVICE
771  HOST_DEBUG | DEVICE_DEBUG
772  HOST_UMPIRE | DEVICE_UMPIRE
773  HOST_PINNED | DEVICE
774  MANAGED | MANAGED
775  DEVICE | HOST
776  DEVICE_DEBUG | HOST_DEBUG
777  DEVICE_UMPIRE | HOST_UMPIRE
778  DEVICE_UMPIRE_2 | HOST_UMPIRE
779 
780  The dual types can be modified before device configuration using the
781  method SetDualMemoryType() or by calling Device::SetMemoryTypes(). */
783  { return dual_map[(int)mt]; }
784 
785  /// Set the dual memory type of @a mt to be @a dual_mt.
786  /** This method can only be called before configuration, i.e. before calling
787  Configure(), which is typically done during Device construction.
788 
789  One of the types must be a host MemoryType and the other must be a device
790  MemoryType or both types must be the same host memory type. The latter
791  case is only allowed for convenience in setting up pure host execution,
792  so the actual dual is not updated. */
793  static void SetDualMemoryType(MemoryType mt, MemoryType dual_mt);
794 
795  /** @brief Configure the Memory manager with given default host and device
796  types. This method will be called when configuring a device.
797 
798  The host and device MemoryType%s, @a h_mt and @a d_mt, are set to be dual
799  to each other. */
800  void Configure(const MemoryType h_mt, const MemoryType d_mt);
801 
802 #ifdef MFEM_USE_UMPIRE
803  /// Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE
804  static void SetUmpireHostAllocatorName(const char * h_name) { h_umpire_name = h_name; }
805  /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
806  static void SetUmpireDeviceAllocatorName(const char * d_name) { d_umpire_name = d_name; }
807  /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
808  static void SetUmpireDevice2AllocatorName(const char * d_name) { d_umpire_2_name = d_name; }
809 
810  /// Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE
811  static const char * GetUmpireHostAllocatorName() { return h_umpire_name; }
812  /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
813  static const char * GetUmpireDeviceAllocatorName() { return d_umpire_name; }
814  /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
815  static const char * GetUmpireDevice2AllocatorName() { return d_umpire_2_name; }
816 #endif
817 
818  /// Free all the device memories
819  void Destroy();
820 
821  /// Return true if the pointer is known by the memory manager
822  bool IsKnown(const void *h_ptr) { return IsKnown_(h_ptr); }
823 
824  /// Return true if the pointer is known by the memory manager as an alias
825  bool IsAlias(const void *h_ptr) { return IsAlias_(h_ptr); }
826 
827  /// Check if the host pointer has been registered in the memory manager
828  void RegisterCheck(void *h_ptr);
829 
830  /// Prints all pointers known by the memory manager,
831  /// returning the number of printed pointers
832  int PrintPtrs(std::ostream &out = mfem::out);
833 
834  /// Prints all aliases known by the memory manager
835  /// returning the number of printed pointers
836  int PrintAliases(std::ostream &out = mfem::out);
837 
838  static MemoryType GetHostMemoryType() { return host_mem_type; }
839  static MemoryType GetDeviceMemoryType() { return device_mem_type; }
840 
841 #ifdef MFEM_USE_ENZYME
842  static void myfree(void* mem, MemoryType MT, unsigned &flags)
843  {
844  MemoryManager::Delete_(mem, MT, flags);
845  }
846  __attribute__((used))
847  inline static void* __enzyme_allocation_like1[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, unsigned&)>(MemoryManager::New_),
848  (void*)1, (void*)"-1,2,3", (void*)myfree
849  };
850  __attribute__((used))
851  inline static void* __enzyme_allocation_like2[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, MemoryType, unsigned, unsigned&)>(MemoryManager::New_),
852  (void*)1, (void*)"-1,2,4", (void*)MemoryManager::Delete_
853  };
854  __attribute__((used))
855  inline static void* __enzyme_function_like[2] = {(void*)MemoryManager::Delete_, (void*)"free"};
856 #endif
857 };
858 
859 
860 // Inline methods
861 
862 template <typename T>
863 inline void Memory<T>::Reset()
864 {
865  h_ptr = NULL;
867  capacity = 0;
868  flags = 0;
869 }
870 
871 template <typename T>
872 inline void Memory<T>::Reset(MemoryType host_mt)
873 {
874  h_ptr = NULL;
875  h_mt = host_mt;
876  capacity = 0;
877  flags = 0;
878 }
879 
880 template <typename T>
881 inline void Memory<T>::New(int size)
882 {
883  capacity = size;
886  h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) :
887  (T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
888 }
889 
890 template <typename T>
891 inline void Memory<T>::New(int size, MemoryType mt)
892 {
893  capacity = size;
894  const size_t bytes = size*sizeof(T);
895  const bool mt_host = mt == MemoryType::HOST;
896  if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
898  T *h_tmp = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
899  h_ptr = (mt_host) ? h_tmp : (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
900 }
901 
902 template <typename T>
903 inline void Memory<T>::New(int size, MemoryType host_mt, MemoryType device_mt)
904 {
905  capacity = size;
906  const size_t bytes = size*sizeof(T);
907  this->h_mt = host_mt;
908  T *h_tmp = (host_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
909  h_ptr = (T*)MemoryManager::New_(h_tmp, bytes, host_mt, device_mt,
910  VALID_HOST, flags);
911 }
912 
913 template <typename T>
914 inline void Memory<T>::Wrap(T *ptr, int size, bool own)
915 {
916  h_ptr = ptr;
917  capacity = size;
918  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
920 #ifdef MFEM_DEBUG
921  if (own && MemoryManager::Exists())
922  {
923  MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_(h_ptr);
924  MFEM_VERIFY(h_mt == h_ptr_mt,
925  "h_mt = " << (int)h_mt << ", h_ptr_mt = " << (int)h_ptr_mt);
926  }
927 #endif
928  if (own && h_mt != MemoryType::HOST)
929  {
930  const size_t bytes = size*sizeof(T);
931  MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags);
932  }
933 }
934 
935 template <typename T>
936 inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
937 {
938  capacity = size;
939  if (IsHostMemory(mt))
940  {
941  h_mt = mt;
942  h_ptr = ptr;
943  if (mt == MemoryType::HOST || !own)
944  {
945  // Skip registration
946  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
947  return;
948  }
949  }
950  else
951  {
953  h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
954  }
955  flags = 0;
956  h_ptr = (T*)MemoryManager::Register_(ptr, h_ptr, size*sizeof(T), mt,
957  own, false, flags);
958 }
959 
960 template <typename T>
961 inline void Memory<T>::Wrap(T *ptr, T *d_ptr, int size, MemoryType mt, bool own)
962 {
963  h_mt = mt;
964  flags = 0;
965  h_ptr = ptr;
966  capacity = size;
967  MFEM_ASSERT(IsHostMemory(h_mt),"");
968  const size_t bytes = size*sizeof(T);
970  MemoryManager::Register2_(h_ptr, d_ptr, bytes, h_mt, d_mt,
971  own, false, flags);
972 }
973 
974 template <typename T>
975 inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
976 {
977  MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
978  MFEM_ASSERT(0 <= size, "invalid size = " << size);
979  MFEM_ASSERT(offset + size <= base.capacity,
980  "invalid offset + size = " << offset + size
981  << " > base capacity = " << base.capacity);
982  capacity = size;
983  h_mt = base.h_mt;
984  h_ptr = base.h_ptr + offset;
985  if (!(base.flags & Registered))
986  {
987  if (
988 #if !defined(HYPRE_USING_GPU)
989  // If the following condition is true then MemoryManager::Exists()
990  // should also be true:
992 #else
993  // When HYPRE_USING_GPU is defined we always register the 'base' if
994  // the MemoryManager::Exists():
995  MemoryManager::Exists()
996 #endif
997  )
998  {
999  // Register 'base':
1000  MemoryManager::Register_(base.h_ptr, nullptr, base.capacity*sizeof(T),
1001  base.h_mt, base.flags & OWNS_HOST,
1002  base.flags & ALIAS, base.flags);
1003  }
1004  else
1005  {
1006  // Copy the flags from 'base', setting the ALIAS flag to true, and
1007  // setting both OWNS_HOST and OWNS_DEVICE to false:
1008  flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
1009  return;
1010  }
1011  }
1012  const size_t s_bytes = size*sizeof(T);
1013  const size_t o_bytes = offset*sizeof(T);
1014  MemoryManager::Alias_(base.h_ptr, o_bytes, s_bytes, base.flags, flags);
1015 }
1016 
1017 template <typename T>
1019 {
1020  if (!IsDeviceMemory(d_mt)) { return; }
1021  if (!(flags & Registered))
1022  {
1023  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1024  flags & OWNS_HOST, flags & ALIAS, flags);
1025  }
1026  MemoryManager::SetDeviceMemoryType_(h_ptr, flags, d_mt);
1027 }
1028 
1029 template <typename T>
1030 inline void Memory<T>::Delete()
1031 {
1032  const bool registered = flags & Registered;
1033  const bool mt_host = h_mt == MemoryType::HOST;
1034  const bool std_delete = !registered && mt_host;
1035 
1036  if (!std_delete)
1037  {
1038  MemoryManager::Delete_((void*)h_ptr, h_mt, flags);
1039  }
1040 
1041  if (mt_host)
1042  {
1043  if (flags & OWNS_HOST) { delete [] h_ptr; }
1044  }
1045  Reset(h_mt);
1046 }
1047 
1048 template <typename T>
1049 inline void Memory<T>::DeleteDevice(bool copy_to_host)
1050 {
1051  if (flags & Registered)
1052  {
1053  if (copy_to_host) { Read(MemoryClass::HOST, capacity); }
1054  MemoryManager::DeleteDevice_((void*)h_ptr, flags);
1055  }
1056 }
1057 
1058 template <typename T>
1059 inline T &Memory<T>::operator[](int idx)
1060 {
1061  MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
1062  "invalid host pointer access");
1063  return h_ptr[idx];
1064 }
1065 
1066 template <typename T>
1067 inline const T &Memory<T>::operator[](int idx) const
1068 {
1069  MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
1070  return h_ptr[idx];
1071 }
1072 
1073 template <typename T>
1075 {
1076  MFEM_ASSERT(Empty() ||
1077  ((flags & VALID_HOST) &&
1078  (std::is_const<T>::value || !(flags & VALID_DEVICE))),
1079  "invalid host pointer access");
1080  return h_ptr;
1081 }
1082 
1083 template <typename T>
1084 inline Memory<T>::operator const T*() const
1085 {
1086  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1087  return h_ptr;
1088 }
1089 
1090 template <typename T> template <typename U>
1092 {
1093  MFEM_ASSERT(Empty() ||
1094  ((flags & VALID_HOST) &&
1095  (std::is_const<U>::value || !(flags & VALID_DEVICE))),
1096  "invalid host pointer access");
1097  return reinterpret_cast<U*>(h_ptr);
1098 }
1099 
1100 template <typename T> template <typename U>
1101 inline Memory<T>::operator const U*() const
1102 {
1103  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1104  return reinterpret_cast<U*>(h_ptr);
1105 }
1106 
1107 template <typename T>
1108 inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
1109 {
1110  const size_t bytes = size * sizeof(T);
1111  if (!(flags & Registered))
1112  {
1113  if (mc == MemoryClass::HOST) { return h_ptr; }
1114  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1115  flags & OWNS_HOST, flags & ALIAS, flags);
1116  }
1117  return (T*)MemoryManager::ReadWrite_(h_ptr, h_mt, mc, bytes, flags);
1118 }
1119 
1120 template <typename T>
1121 inline const T *Memory<T>::Read(MemoryClass mc, int size) const
1122 {
1123  const size_t bytes = size * sizeof(T);
1124  if (!(flags & Registered))
1125  {
1126  if (mc == MemoryClass::HOST) { return h_ptr; }
1127  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1128  flags & OWNS_HOST, flags & ALIAS, flags);
1129  }
1130  return (const T*)MemoryManager::Read_(h_ptr, h_mt, mc, bytes, flags);
1131 }
1132 
1133 template <typename T>
1134 inline T *Memory<T>::Write(MemoryClass mc, int size)
1135 {
1136  const size_t bytes = size * sizeof(T);
1137  if (!(flags & Registered))
1138  {
1139  if (mc == MemoryClass::HOST) { return h_ptr; }
1140  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1141  flags & OWNS_HOST, flags & ALIAS, flags);
1142  }
1143  return (T*)MemoryManager::Write_(h_ptr, h_mt, mc, bytes, flags);
1144 }
1145 
1146 template <typename T>
1147 inline void Memory<T>::Sync(const Memory &other) const
1148 {
1149  if (!(flags & Registered) && (other.flags & Registered))
1150  {
1151  MFEM_ASSERT(h_ptr == other.h_ptr &&
1152  (flags & ALIAS) == (other.flags & ALIAS),
1153  "invalid input");
1155  }
1156  flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
1157  (other.flags & (VALID_HOST | VALID_DEVICE));
1158 }
1159 
1160 template <typename T>
1161 inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
1162 {
1163  // Assuming that if *this is registered then base is also registered.
1164  MFEM_ASSERT(!(flags & Registered) || (base.flags & Registered),
1165  "invalid base state");
1166  if (!(base.flags & Registered)) { return; }
1167  MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
1168  base.flags, flags);
1169 }
1170 
1171 template <typename T>
1173 {
1174  if (h_ptr == nullptr || !(flags & VALID_DEVICE)) { return h_mt; }
1175  return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1176 }
1177 
1178 template <typename T>
1180 {
1181  if (!(flags & Registered)) { return MemoryType::DEFAULT; }
1182  return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1183 }
1184 
1185 template <typename T>
1186 inline bool Memory<T>::HostIsValid() const
1187 {
1188  return flags & VALID_HOST ? true : false;
1189 }
1190 
1191 template <typename T>
1192 inline bool Memory<T>::DeviceIsValid() const
1193 {
1194  return flags & VALID_DEVICE ? true : false;
1195 }
1196 
1197 template <typename T>
1198 inline void Memory<T>::CopyFrom(const Memory &src, int size)
1199 {
1200  MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
1201  if (!(flags & Registered) && !(src.flags & Registered))
1202  {
1203  if (h_ptr != src.h_ptr && size != 0)
1204  {
1205  MFEM_ASSERT(h_ptr + size <= src.h_ptr || src.h_ptr + size <= h_ptr,
1206  "data overlaps!");
1207  std::memcpy(h_ptr, src, size*sizeof(T));
1208  }
1209  // *this is not registered, so (flags & VALID_HOST) must be true
1210  }
1211  else
1212  {
1213  MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
1214  }
1215 }
1216 
1217 template <typename T>
1218 inline void Memory<T>::CopyFromHost(const T *src, int size)
1219 {
1220  MFEM_VERIFY(capacity>=size, "Incorrect size");
1221  if (!(flags & Registered))
1222  {
1223  if (h_ptr != src && size != 0)
1224  {
1225  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
1226  "data overlaps!");
1227  std::memcpy(h_ptr, src, size*sizeof(T));
1228  }
1229  // *this is not registered, so (flags & VALID_HOST) must be true
1230  }
1231  else
1232  {
1233  MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
1234  }
1235 }
1236 
1237 template <typename T>
1238 inline void Memory<T>::CopyTo(Memory &dest, int size) const
1239 {
1240  MFEM_VERIFY(capacity>=size, "Incorrect size");
1241  dest.CopyFrom(*this, size);
1242 }
1243 
1244 template <typename T>
1245 inline void Memory<T>::CopyToHost(T *dest, int size) const
1246 {
1247  MFEM_VERIFY(capacity>=size, "Incorrect size");
1248  if (!(flags & Registered))
1249  {
1250  if (h_ptr != dest && size != 0)
1251  {
1252  MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
1253  "data overlaps!");
1254  std::memcpy(dest, h_ptr, size*sizeof(T));
1255  }
1256  }
1257  else
1258  {
1259  MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
1260  }
1261 }
1262 
1263 
1264 /** @brief Print the state of a Memory object based on its internal flags.
1265  Useful in a debugger. See also Memory<T>::PrintFlags(). */
1266 extern void MemoryPrintFlags(unsigned flags);
1267 
1268 
1269 template <typename T>
1270 inline void Memory<T>::PrintFlags() const
1271 {
1273 }
1274 
1275 template <typename T>
1276 inline int Memory<T>::CompareHostAndDevice(int size) const
1277 {
1278  if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
1279  return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
1280 }
1281 
1282 
1283 /// The (single) global memory manager object
1284 extern MFEM_EXPORT MemoryManager mm;
1285 
1286 } // namespace mfem
1287 
1288 #endif // MFEM_MEM_MANAGER_HPP
MemoryType h_mt
Host memory type.
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
Memory(Memory &&orig)
Host memory; aligned at 64 bytes.
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
Definition: mem_manager.hpp:85
bool DeviceIsValid() const
Return true if device pointer is valid.
~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. See also Memory<T>::PrintFlags().
int CompareHostAndDevice(int size) const
If both the host and the device data are valid, compare their contents.
Device memory; using CUDA or HIP *Malloc and *Free.
static const char * GetUmpireHostAllocatorName()
Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE.
void Delete()
Delete the owned pointers and reset the Memory object.
MemoryType GetHostMemoryType() const
Return the host MemoryType of the Memory object.
Memory(T *ptr, int size, MemoryType mt, bool own)
Wrap an externally allocated pointer, ptr, of the given MemoryType.
static MemoryType GetHostMemoryType()
static void myfree(void *mem, MemoryType MT, unsigned &flags)
Host pointer is valid.
Host memory; allocated from a "host-debug" pool.
MemoryManager mm
The (single) global memory manager object.
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
Memory(int size)
Allocate host memory for size entries.
STL namespace.
Host memory: pinned (page-locked)
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
Memory & operator=(Memory &&orig)
Number of host and device memory types.
bool HostIsValid() const
Return true if host pointer is valid.
Memory(int size, MemoryType mt)
Allocate memory for size entries with the given MemoryType mt.
unsigned flags
Bit flags defined from the FlagMask enum.
bool IsAlias(const void *h_ptr)
Return true if the pointer is known by the memory manager as an alias.
class if defined(__alignas_is_defined) alignas(double) RowNode
Definition: sparsemat.hpp:38
bool MemoryClassContainsType(MemoryClass mc, MemoryType mt)
Return true iff the MemoryType mt is contained in the MemoryClass mc.
Definition: mem_manager.cpp:70
Host memory; aligned at 32 bytes.
void Wrap(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
static MemoryType GetDualMemoryType(MemoryType mt)
Return the dual MemoryType of the given one, mt.
void SetDevicePtrOwner(bool own) const
Set/clear the ownership flag for the device pointer. Ownership indicates whether the pointer will be ...
void CopyFromHost(const T *src, int size)
Copy size entries from the host pointer src to *this.
bool Empty() const
Return true if the Memory object is empty, see Reset().
constexpr int DeviceMemoryType
Definition: mem_manager.hpp:63
static const char * GetUmpireDeviceAllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE.
void Sync(const Memory &other) const
Copy the host/device pointer validity flags from other to *this.
constexpr int HostMemoryType
Definition: mem_manager.hpp:61
static MemoryType GetDeviceMemoryType()
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...
Memory(const Memory &base, int offset, int size)
Alias constructor. Create a Memory object that points inside the Memory object base.
Ownership flag for internal Memory data.
Device pointer is valid
The host pointer will be deleted by Delete()
constexpr int MemoryTypeSize
Static casts to &#39;int&#39; and sizes of some useful memory types.
Definition: mem_manager.hpp:60
Memory & operator=(const Memory &orig)=default
Copy-assignment operator: default.
int Capacity() const
Return the size of the allocated memory.
bool OwnsDevicePtr() const
Return true if the device pointer is owned. Ownership indicates whether the pointer will be deleted b...
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
void CopyToHost(T *dest, int size) const
Copy size entries from *this to the host pointer dest.
static const char * GetUmpireDevice2AllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:31
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
void SetDeviceMemoryType(MemoryType d_mt)
Set the device MemoryType to be used by the Memory object.
bool IsKnown(const void *h_ptr)
Return true if the pointer is known by the memory manager.
void DeleteDevice(bool copy_to_host=true)
Delete the device pointer, if owned. If copy_to_host is true and the data is valid only on device...
A class to initialize the size of a Tensor.
Definition: dtensor.hpp:54
constexpr int HostMemoryTypeSize
Definition: mem_manager.hpp:62
void MakeAlias(const Memory &base, int offset, int size)
Create a memory object that points inside the memory object base.
bool IsDeviceMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::DEVICE.
Definition: mem_manager.hpp:88
Pointer is an alias.
static void SetUmpireDevice2AllocatorName(const char *d_name)
Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2.
T * h_ptr
Pointer to host memory. Not owned.
int capacity
Size of the allocated memory.
Memory(int size, MemoryType h_mt, MemoryType d_mt)
Allocate memory for size entries with the given host MemoryType h_mt and device MemoryType d_mt...
Host memory; using new[] and delete[].
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
void PrintFlags() const
Print the internal flags.
MemoryType GetDeviceMemoryType() const
Return the device MemoryType of the Memory object. If the device MemoryType is not set...
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
friend class MemoryManager
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
Definition: mem_manager.cpp:55
Class used by MFEM to store pointers to host and/or device memory.
static void SetUmpireHostAllocatorName(const char *h_name)
Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE.
bool OwnsHostPtr() const
Return true if the host pointer is owned. Ownership indicates whether the pointer will be deleted by ...
void CopyTo(Memory &dest, int size) const
Copy size entries from *this to dest.
bool UseDevice() const
Read the internal device flag.
void UseDevice(bool use_dev) const
Set the internal device flag.
constexpr int DeviceMemoryTypeSize
Definition: mem_manager.hpp:64
Memory(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
T & operator[](int idx)
Array subscript operator for host memory.
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
Return a suitable MemoryClass from a pair of MemoryClasses.
static void SetUmpireDeviceAllocatorName(const char *d_name)
Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE.
MemoryClass
Memory classes identify sets of memory types.
Definition: mem_manager.hpp:73
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...