MFEM  v4.6.0
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 unless
371  specified otherwise by the parameters @a valid_host and @a valid_device.
372 
373  The given @a h_ptr and @a d_ptr must be allocated appropriately for the
374  given host MemoryType and its dual device MemoryType as defined by
375  MemoryManager::GetDualMemoryType().
376 
377  The parameter @a own determines whether both @a h_ptr and @a d_ptr will
378  be deleted when the method Delete() is called.
379 
380  The parameters @a valid_host and @a valid_device determine which
381  pointers, host and/or device, will be marked as valid; at least one of
382  the two parameters must be set to true.
383 
384  @note Ownership can also be controlled by using the following methods:
385  - ClearOwnerFlags,
386  - SetHostPtrOwner,
387  - SetDevicePtrOwner.
388 
389  @note The current memory is NOT deleted by this method. */
390  inline void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own,
391  bool valid_host = false, bool valid_device = true);
392 
393  /// Create a memory object that points inside the memory object @a base.
394  /** The new Memory object uses the same MemoryType(s) as @a base.
395 
396  @note The current memory is NOT deleted by this method. */
397  inline void MakeAlias(const Memory &base, int offset, int size);
398 
399  /// Set the device MemoryType to be used by the Memory object.
400  /** If the specified @a d_mt is not a device MemoryType, i.e. not one of the
401  types in MemoryClass::DEVICE, then this method will return immediately.
402 
403  If the device MemoryType has been previously set to a different type and
404  the actual device memory has been allocated, this method will trigger an
405  error. This method will not perform the actual device memory allocation,
406  however, the allocation may already exist if the MemoryType is the same
407  as the current one.
408 
409  If the Memory is an alias Memory, the device MemoryType of its base will
410  be updated as described above. */
411  inline void SetDeviceMemoryType(MemoryType d_mt);
412 
413  /** @brief Delete the owned pointers and reset the Memory object. */
414  inline void Delete();
415 
416  /** @brief Delete the device pointer, if owned. If @a copy_to_host is true
417  and the data is valid only on device, move it to host before deleting.
418  Invalidates the device memory. */
419  inline void DeleteDevice(bool copy_to_host = true);
420 
421  /// Array subscript operator for host memory.
422  inline T &operator[](int idx);
423 
424  /// Array subscript operator for host memory, const version.
425  inline const T &operator[](int idx) const;
426 
427  /// Direct access to the host memory as T* (implicit conversion).
428  /** When the type T is const-qualified, this method can be used only if the
429  host pointer is currently valid (the device pointer may be valid or
430  invalid).
431 
432  When the type T is not const-qualified, this method can be used only if
433  the host pointer is the only valid pointer.
434 
435  When the Memory is empty, this method can be used and it returns NULL. */
436  inline operator T*();
437 
438  /// Direct access to the host memory as const T* (implicit conversion).
439  /** This method can be used only if the host pointer is currently valid (the
440  device pointer may be valid or invalid).
441 
442  When the Memory is empty, this method can be used and it returns NULL. */
443  inline operator const T*() const;
444 
445  /// Direct access to the host memory via explicit typecast.
446  /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
447  In particular, this method cannot be used to cast away const-ness from
448  the base type T.
449 
450  When the type U is const-qualified, this method can be used only if the
451  host pointer is currently valid (the device pointer may be valid or
452  invalid).
453 
454  When the type U is not const-qualified, this method can be used only if
455  the host pointer is the only valid pointer.
456 
457  When the Memory is empty, this method can be used and it returns NULL. */
458  template <typename U>
459  inline explicit operator U*();
460 
461  /// Direct access to the host memory via explicit typecast, const version.
462  /** A pointer to type T must be reinterpret_cast-able to a pointer to type
463  const U.
464 
465  This method can be used only if the host pointer is currently valid (the
466  device pointer may be valid or invalid).
467 
468  When the Memory is empty, this method can be used and it returns NULL. */
469  template <typename U>
470  inline explicit operator const U*() const;
471 
472  /// Get read-write access to the memory with the given MemoryClass.
473  /** If only read or only write access is needed, then the methods
474  Read() or Write() should be used instead of this method.
475 
476  The parameter @a size must not exceed the Capacity(). */
477  inline T *ReadWrite(MemoryClass mc, int size);
478 
479  /// Get read-only access to the memory with the given MemoryClass.
480  /** The parameter @a size must not exceed the Capacity(). */
481  inline const T *Read(MemoryClass mc, int size) const;
482 
483  /// Get write-only access to the memory with the given MemoryClass.
484  /** The parameter @a size must not exceed the Capacity().
485 
486  The contents of the returned pointer is undefined, unless it was
487  validated by a previous call to Read() or ReadWrite() with
488  the same MemoryClass. */
489  inline T *Write(MemoryClass mc, int size);
490 
491  /// Copy the host/device pointer validity flags from @a other to @a *this.
492  /** This method synchronizes the pointer validity flags of two Memory objects
493  that use the same host/device pointers, or when @a *this is an alias
494  (sub-Memory) of @a other. Typically, this method should be called after
495  @a other is manipulated in a way that changes its pointer validity flags
496  (e.g. it was moved from device to host memory). */
497  inline void Sync(const Memory &other) const;
498 
499  /** @brief Update the alias Memory @a *this to match the memory location (all
500  valid locations) of its base Memory, @a base. */
501  /** This method is useful when alias Memory is moved and manipulated in a
502  different memory space. Such operations render the pointer validity flags
503  of the base incorrect. Calling this method will ensure that @a base is
504  up-to-date. Note that this is achieved by moving/copying @a *this (if
505  necessary), and not @a base. */
506  inline void SyncAlias(const Memory &base, int alias_size) const;
507 
508  /** @brief Return a MemoryType that is currently valid. If both the host and
509  the device pointers are currently valid, then the device memory type is
510  returned. */
511  inline MemoryType GetMemoryType() const;
512 
513  /// Return the host MemoryType of the Memory object.
514  inline MemoryType GetHostMemoryType() const { return h_mt; }
515 
516  /** @brief Return the device MemoryType of the Memory object. If the device
517  MemoryType is not set, return MemoryType::DEFAULT. */
518  inline MemoryType GetDeviceMemoryType() const;
519 
520  /** @brief Return true if host pointer is valid */
521  inline bool HostIsValid() const;
522 
523  /** @brief Return true if device pointer is valid */
524  inline bool DeviceIsValid() const;
525 
526  /// Copy @a size entries from @a src to @a *this.
527  /** The given @a size should not exceed the Capacity() of the source @a src
528  and the destination, @a *this. */
529  inline void CopyFrom(const Memory &src, int size);
530 
531  /// Copy @a size entries from the host pointer @a src to @a *this.
532  /** The given @a size should not exceed the Capacity() of @a *this. */
533  inline void CopyFromHost(const T *src, int size);
534 
535  /// Copy @a size entries from @a *this to @a dest.
536  /** The given @a size should not exceed the Capacity() of @a *this and the
537  destination, @a dest. */
538  inline void CopyTo(Memory &dest, int size) const;
539 
540  /// Copy @a size entries from @a *this to the host pointer @a dest.
541  /** The given @a size should not exceed the Capacity() of @a *this. */
542  inline void CopyToHost(T *dest, int size) const;
543 
544  /// Print the internal flags.
545  /** This method can be useful for debugging. It is explicitly instantiated
546  for Memory<T> with T = int and T = double. */
547  inline void PrintFlags() const;
548 
549  /// If both the host and the device data are valid, compare their contents.
550  /** This method can be useful for debugging. It is explicitly instantiated
551  for Memory<T> with T = int and T = double. */
552  inline int CompareHostAndDevice(int size) const;
553 
554 private:
555  // GCC 4.8 workaround: max_align_t is not in std.
556  static constexpr std::size_t def_align_bytes_()
557  {
558  using namespace std;
559  return alignof(max_align_t);
560  }
561  static constexpr std::size_t def_align_bytes = def_align_bytes_();
562  static constexpr std::size_t new_align_bytes =
563  alignof(T) > def_align_bytes ? alignof(T) : def_align_bytes;
564 
565  template <std::size_t align_bytes, bool dummy = true> struct Alloc
566  {
567 #if __cplusplus < 201703L
568  static inline T *New(std::size_t)
569  {
570  // Generate an error in debug mode
571  MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
572  return nullptr;
573  }
574 #else
575  static inline T *New(std::size_t size) { return new T[size]; }
576 #endif
577  };
578 
579 #if __cplusplus < 201703L
580  template<bool dummy> struct Alloc<def_align_bytes,dummy>
581  {
582  static inline T *New(std::size_t size) { return new T[size]; }
583  };
584 #endif
585 
586  // Shortcut for Alloc<new_align_bytes>::New(size)
587  static inline T *NewHOST(std::size_t size)
588  {
589  return Alloc<new_align_bytes>::New(size);
590  }
591 };
592 
593 
594 /** The MFEM memory manager class. Host-side pointers are inserted into this
595  manager which keeps track of the associated device pointer, and where the
596  data currently resides. */
597 class MFEM_EXPORT MemoryManager
598 {
599 private:
600 
601  typedef MemoryType MemType;
602  typedef Memory<int> Mem;
603 
604  template <typename T> friend class Memory;
605 
606  /// Host memory type set during the Setup.
607  static MemoryType host_mem_type;
608 
609  /// Device memory type set during the Setup.
610  static MemoryType device_mem_type;
611 
612  /// Allow to detect if a global memory manager instance exists.
613  static bool exists;
614 
615  /// Return true if the global memory manager instance exists.
616  static bool Exists() { return exists; }
617 
618  /// Array defining the dual MemoryType for each MemoryType
619  /** The dual of a host MemoryType is a device MemoryType and vice versa: the
620  dual of a device MemoryType is a host MemoryType. */
621  static MemoryType dual_map[MemoryTypeSize];
622 
623  /// Update the dual memory type of @a mt to be @a dual_mt.
624  static void UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt);
625 
626  /// True if Configure() was called.
627  static bool configured;
628 
629  /// Host and device allocator names for Umpire.
630 #ifdef MFEM_USE_UMPIRE
631  static const char * h_umpire_name;
632  static const char * d_umpire_name;
633  static const char * d_umpire_2_name;
634 #endif
635 
636 private: // Static methods used by the Memory<T> class
637 
638  /// Allocate and register a new pointer. Return the host pointer.
639  /// h_tmp must be already allocated using new T[] if mt is a pure device
640  /// memory type, e.g. CUDA (mt will not be HOST).
641  static void *New_(void *h_tmp, size_t bytes, MemoryType mt, unsigned &flags);
642 
643  static void *New_(void *h_tmp, size_t bytes, MemoryType h_mt,
644  MemoryType d_mt, unsigned valid_flags, unsigned &flags);
645 
646  /// Register an external pointer of the given MemoryType.
647  /// Return the host pointer.
648  static void *Register_(void *ptr, void *h_ptr, size_t bytes, MemoryType mt,
649  bool own, bool alias, unsigned &flags);
650 
651  /// Register a pair of external host and device pointers
652  static void Register2_(void *h_ptr, void *d_ptr, size_t bytes,
653  MemoryType h_mt, MemoryType d_mt,
654  bool own, bool alias, unsigned &flags,
655  unsigned valid_flags);
656 
657  /// Register an alias. Note: base_h_ptr may be an alias.
658  static void Alias_(void *base_h_ptr, size_t offset, size_t bytes,
659  unsigned base_flags, unsigned &flags);
660 
661  static void SetDeviceMemoryType_(void *h_ptr, unsigned flags,
662  MemoryType d_mt);
663 
664  /// Un-register and free memory identified by its host pointer.
665  static void Delete_(void *h_ptr, MemoryType mt, unsigned flags);
666 
667  /// Free device memory identified by its host pointer
668  static void DeleteDevice_(void *h_ptr, unsigned & flags);
669 
670  /// Check if the memory types given the memory class are valid
671  static bool MemoryClassCheck_(MemoryClass mc, void *h_ptr,
672  MemoryType h_mt, size_t bytes, unsigned flags);
673 
674  /// Return a pointer to the memory identified by the host pointer h_ptr for
675  /// access with the given MemoryClass.
676  static void *ReadWrite_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
677  size_t bytes, unsigned &flags);
678 
679  static const void *Read_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
680  size_t bytes, unsigned &flags);
681 
682  static void *Write_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
683  size_t bytes, unsigned &flags);
684 
685  static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
686  size_t alias_bytes, unsigned base_flags,
687  unsigned &alias_flags);
688 
689  /// Return the type the of the currently valid memory.
690  /// If more than one types are valid, return a device type.
691  static MemoryType GetDeviceMemoryType_(void *h_ptr, bool alias);
692 
693  /// Return the type the of the host memory.
694  static MemoryType GetHostMemoryType_(void *h_ptr);
695 
696  /// Verify that h_mt and h_ptr's h_mt (memory or alias) are equal.
697  static void CheckHostMemoryType_(MemoryType h_mt, void *h_ptr, bool alias);
698 
699  /// Copy entries from valid memory type to valid memory type.
700  /// Both dest_h_ptr and src_h_ptr are registered host pointers.
701  static void Copy_(void *dest_h_ptr, const void *src_h_ptr, size_t bytes,
702  unsigned src_flags, unsigned &dest_flags);
703 
704  /// Copy entries from valid memory type to host memory, where dest_h_ptr is
705  /// not a registered host pointer and src_h_ptr is a registered host pointer.
706  static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
707  size_t bytes, unsigned src_flags);
708 
709  /// Copy entries from host memory to valid memory type, where dest_h_ptr is a
710  /// registered host pointer and src_h_ptr is not a registered host pointer.
711  static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
712  size_t bytes, unsigned &dest_flags);
713 
714  /// Check if the host pointer has been registered in the memory manager.
715  static bool IsKnown_(const void *h_ptr);
716 
717  /** @brief Check if the host pointer has been registered as an alias in the
718  memory manager. */
719  static bool IsAlias_(const void *h_ptr);
720 
721  /// Compare the contents of the host and the device memory.
722  static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
723 
724 private:
725 
726  /// Insert a host address @a h_ptr and size *a bytes in the memory map to be
727  /// managed.
728  void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
729 
730  /// Insert a device and the host addresses in the memory map
731  void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
732  MemoryType h_mt, MemoryType d_mt);
733 
734  /// Insert an alias in the alias map
735  void InsertAlias(const void *base_ptr, void *alias_ptr,
736  const size_t bytes, const bool base_is_alias);
737 
738  /// Erase an address from the memory map, as well as all its aliases
739  void Erase(void *h_ptr, bool free_dev_ptr = true);
740 
741  /// Erase device memory for a given host address
742  void EraseDevice(void *h_ptr);
743 
744  /// Erase an alias from the aliases map
745  void EraseAlias(void *alias_ptr);
746 
747  /// Return the corresponding device pointer of h_ptr,
748  /// allocating and moving the data if needed
749  void *GetDevicePtr(const void *h_ptr, size_t bytes, bool copy_data);
750 
751  /// Return the corresponding device pointer of alias_ptr,
752  /// allocating and moving the data if needed
753  void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
754 
755  /// Return the corresponding host pointer of d_ptr,
756  /// allocating and moving the data if needed
757  void *GetHostPtr(const void *d_ptr, size_t bytes, bool copy_data);
758 
759  /// Return the corresponding host pointer of alias_ptr,
760  /// allocating and moving the data if needed
761  void *GetAliasHostPtr(const void *alias_ptr, size_t bytes, bool copy_data);
762 
763 public:
764  MemoryManager();
765  ~MemoryManager();
766 
767  /// Initialize the memory manager.
768  void Init();
769 
770  /// Return the dual MemoryType of the given one, @a mt.
771  /** The default dual memory types are:
772 
773  memory type | dual type
774  --------------- | ---------
775  HOST | DEVICE
776  HOST_32 | DEVICE
777  HOST_64 | DEVICE
778  HOST_DEBUG | DEVICE_DEBUG
779  HOST_UMPIRE | DEVICE_UMPIRE
780  HOST_PINNED | DEVICE
781  MANAGED | MANAGED
782  DEVICE | HOST
783  DEVICE_DEBUG | HOST_DEBUG
784  DEVICE_UMPIRE | HOST_UMPIRE
785  DEVICE_UMPIRE_2 | HOST_UMPIRE
786 
787  The dual types can be modified before device configuration using the
788  method SetDualMemoryType() or by calling Device::SetMemoryTypes(). */
790  { return dual_map[(int)mt]; }
791 
792  /// Set the dual memory type of @a mt to be @a dual_mt.
793  /** This method can only be called before configuration, i.e. before calling
794  Configure(), which is typically done during Device construction.
795 
796  One of the types must be a host MemoryType and the other must be a device
797  MemoryType or both types must be the same host memory type. The latter
798  case is only allowed for convenience in setting up pure host execution,
799  so the actual dual is not updated. */
800  static void SetDualMemoryType(MemoryType mt, MemoryType dual_mt);
801 
802  /** @brief Configure the Memory manager with given default host and device
803  types. This method will be called when configuring a device.
804 
805  The host and device MemoryType%s, @a h_mt and @a d_mt, are set to be dual
806  to each other. */
807  void Configure(const MemoryType h_mt, const MemoryType d_mt);
808 
809 #ifdef MFEM_USE_UMPIRE
810  /// Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE
811  static void SetUmpireHostAllocatorName(const char * h_name) { h_umpire_name = h_name; }
812  /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
813  static void SetUmpireDeviceAllocatorName(const char * d_name) { d_umpire_name = d_name; }
814  /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
815  static void SetUmpireDevice2AllocatorName(const char * d_name) { d_umpire_2_name = d_name; }
816 
817  /// Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE
818  static const char * GetUmpireHostAllocatorName() { return h_umpire_name; }
819  /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
820  static const char * GetUmpireDeviceAllocatorName() { return d_umpire_name; }
821  /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
822  static const char * GetUmpireDevice2AllocatorName() { return d_umpire_2_name; }
823 #endif
824 
825  /// Free all the device memories
826  void Destroy();
827 
828  /// Return true if the pointer is known by the memory manager
829  bool IsKnown(const void *h_ptr) { return IsKnown_(h_ptr); }
830 
831  /// Return true if the pointer is known by the memory manager as an alias
832  bool IsAlias(const void *h_ptr) { return IsAlias_(h_ptr); }
833 
834  /// Check if the host pointer has been registered in the memory manager
835  void RegisterCheck(void *h_ptr);
836 
837  /// Prints all pointers known by the memory manager,
838  /// returning the number of printed pointers
839  int PrintPtrs(std::ostream &out = mfem::out);
840 
841  /// Prints all aliases known by the memory manager
842  /// returning the number of printed pointers
843  int PrintAliases(std::ostream &out = mfem::out);
844 
845  static MemoryType GetHostMemoryType() { return host_mem_type; }
846  static MemoryType GetDeviceMemoryType() { return device_mem_type; }
847 
848 #ifdef MFEM_USE_ENZYME
849  static void myfree(void* mem, MemoryType MT, unsigned &flags)
850  {
851  MemoryManager::Delete_(mem, MT, flags);
852  }
853  __attribute__((used))
854  inline static void* __enzyme_allocation_like1[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, unsigned&)>(MemoryManager::New_),
855  (void*)1, (void*)"-1,2,3", (void*)myfree
856  };
857  __attribute__((used))
858  inline static void* __enzyme_allocation_like2[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, MemoryType, unsigned, unsigned&)>(MemoryManager::New_),
859  (void*)1, (void*)"-1,2,4", (void*)MemoryManager::Delete_
860  };
861  __attribute__((used))
862  inline static void* __enzyme_function_like[2] = {(void*)MemoryManager::Delete_, (void*)"free"};
863 #endif
864 };
865 
866 
867 // Inline methods
868 
869 template <typename T>
870 inline void Memory<T>::Reset()
871 {
872  h_ptr = NULL;
874  capacity = 0;
875  flags = 0;
876 }
877 
878 template <typename T>
879 inline void Memory<T>::Reset(MemoryType host_mt)
880 {
881  h_ptr = NULL;
882  h_mt = host_mt;
883  capacity = 0;
884  flags = 0;
885 }
886 
887 template <typename T>
888 inline void Memory<T>::New(int size)
889 {
890  capacity = size;
893  h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) :
894  (T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
895 }
896 
897 template <typename T>
898 inline void Memory<T>::New(int size, MemoryType mt)
899 {
900  capacity = size;
901  const size_t bytes = size*sizeof(T);
902  const bool mt_host = mt == MemoryType::HOST;
903  if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
905  T *h_tmp = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
906  h_ptr = (mt_host) ? h_tmp : (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
907 }
908 
909 template <typename T>
910 inline void Memory<T>::New(int size, MemoryType host_mt, MemoryType device_mt)
911 {
912  capacity = size;
913  const size_t bytes = size*sizeof(T);
914  this->h_mt = host_mt;
915  T *h_tmp = (host_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
916  h_ptr = (T*)MemoryManager::New_(h_tmp, bytes, host_mt, device_mt,
917  VALID_HOST, flags);
918 }
919 
920 template <typename T>
921 inline void Memory<T>::Wrap(T *ptr, int size, bool own)
922 {
923  h_ptr = ptr;
924  capacity = size;
925  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
927 #ifdef MFEM_DEBUG
928  if (own && MemoryManager::Exists())
929  {
930  MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_(h_ptr);
931  MFEM_VERIFY(h_mt == h_ptr_mt,
932  "h_mt = " << (int)h_mt << ", h_ptr_mt = " << (int)h_ptr_mt);
933  }
934 #endif
935  if (own && h_mt != MemoryType::HOST)
936  {
937  const size_t bytes = size*sizeof(T);
938  MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags);
939  }
940 }
941 
942 template <typename T>
943 inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
944 {
945  capacity = size;
946  if (IsHostMemory(mt))
947  {
948  h_mt = mt;
949  h_ptr = ptr;
950  if (mt == MemoryType::HOST || !own)
951  {
952  // Skip registration
953  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
954  return;
955  }
956  }
957  else
958  {
960  h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
961  }
962  flags = 0;
963  h_ptr = (T*)MemoryManager::Register_(ptr, h_ptr, size*sizeof(T), mt,
964  own, false, flags);
965 }
966 
967 template <typename T>
968 inline void Memory<T>::Wrap(T *h_ptr_, T *d_ptr, int size, MemoryType h_mt_,
969  bool own, bool valid_host, bool valid_device)
970 {
971  h_mt = h_mt_;
972  flags = 0;
973  h_ptr = h_ptr_;
974  capacity = size;
975  MFEM_ASSERT(IsHostMemory(h_mt),"");
976  MFEM_ASSERT(valid_host || valid_device,"");
977  const size_t bytes = size*sizeof(T);
979  MemoryManager::Register2_(h_ptr, d_ptr, bytes, h_mt, d_mt,
980  own, false, flags,
981  valid_host*VALID_HOST|valid_device*VALID_DEVICE);
982 }
983 
984 template <typename T>
985 inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
986 {
987  MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
988  MFEM_ASSERT(0 <= size, "invalid size = " << size);
989  MFEM_ASSERT(offset + size <= base.capacity,
990  "invalid offset + size = " << offset + size
991  << " > base capacity = " << base.capacity);
992  capacity = size;
993  h_mt = base.h_mt;
994  h_ptr = base.h_ptr + offset;
995  if (!(base.flags & Registered))
996  {
997  if (
998 #if !defined(HYPRE_USING_GPU)
999  // If the following condition is true then MemoryManager::Exists()
1000  // should also be true:
1002 #else
1003  // When HYPRE_USING_GPU is defined we always register the 'base' if
1004  // the MemoryManager::Exists():
1005  MemoryManager::Exists()
1006 #endif
1007  )
1008  {
1009  // Register 'base':
1010  MemoryManager::Register_(base.h_ptr, nullptr, base.capacity*sizeof(T),
1011  base.h_mt, base.flags & OWNS_HOST,
1012  base.flags & ALIAS, base.flags);
1013  }
1014  else
1015  {
1016  // Copy the flags from 'base', setting the ALIAS flag to true, and
1017  // setting both OWNS_HOST and OWNS_DEVICE to false:
1018  flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
1019  return;
1020  }
1021  }
1022  const size_t s_bytes = size*sizeof(T);
1023  const size_t o_bytes = offset*sizeof(T);
1024  MemoryManager::Alias_(base.h_ptr, o_bytes, s_bytes, base.flags, flags);
1025 }
1026 
1027 template <typename T>
1029 {
1030  if (!IsDeviceMemory(d_mt)) { return; }
1031  if (!(flags & Registered))
1032  {
1033  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1034  flags & OWNS_HOST, flags & ALIAS, flags);
1035  }
1036  MemoryManager::SetDeviceMemoryType_(h_ptr, flags, d_mt);
1037 }
1038 
1039 template <typename T>
1040 inline void Memory<T>::Delete()
1041 {
1042  const bool registered = flags & Registered;
1043  const bool mt_host = h_mt == MemoryType::HOST;
1044  const bool std_delete = !registered && mt_host;
1045 
1046  if (!std_delete)
1047  {
1048  MemoryManager::Delete_((void*)h_ptr, h_mt, flags);
1049  }
1050 
1051  if (mt_host)
1052  {
1053  if (flags & OWNS_HOST) { delete [] h_ptr; }
1054  }
1055  Reset(h_mt);
1056 }
1057 
1058 template <typename T>
1059 inline void Memory<T>::DeleteDevice(bool copy_to_host)
1060 {
1061  if (flags & Registered)
1062  {
1063  if (copy_to_host) { Read(MemoryClass::HOST, capacity); }
1064  MemoryManager::DeleteDevice_((void*)h_ptr, flags);
1065  }
1066 }
1067 
1068 template <typename T>
1069 inline T &Memory<T>::operator[](int idx)
1070 {
1071  MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
1072  "invalid host pointer access");
1073  return h_ptr[idx];
1074 }
1075 
1076 template <typename T>
1077 inline const T &Memory<T>::operator[](int idx) const
1078 {
1079  MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
1080  return h_ptr[idx];
1081 }
1082 
1083 template <typename T>
1085 {
1086  MFEM_ASSERT(Empty() ||
1087  ((flags & VALID_HOST) &&
1088  (std::is_const<T>::value || !(flags & VALID_DEVICE))),
1089  "invalid host pointer access");
1090  return h_ptr;
1091 }
1092 
1093 template <typename T>
1094 inline Memory<T>::operator const T*() const
1095 {
1096  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1097  return h_ptr;
1098 }
1099 
1100 template <typename T> template <typename U>
1102 {
1103  MFEM_ASSERT(Empty() ||
1104  ((flags & VALID_HOST) &&
1105  (std::is_const<U>::value || !(flags & VALID_DEVICE))),
1106  "invalid host pointer access");
1107  return reinterpret_cast<U*>(h_ptr);
1108 }
1109 
1110 template <typename T> template <typename U>
1111 inline Memory<T>::operator const U*() const
1112 {
1113  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1114  return reinterpret_cast<U*>(h_ptr);
1115 }
1116 
1117 template <typename T>
1118 inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
1119 {
1120  const size_t bytes = size * sizeof(T);
1121  if (!(flags & Registered))
1122  {
1123  if (mc == MemoryClass::HOST) { return h_ptr; }
1124  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1125  flags & OWNS_HOST, flags & ALIAS, flags);
1126  }
1127  return (T*)MemoryManager::ReadWrite_(h_ptr, h_mt, mc, bytes, flags);
1128 }
1129 
1130 template <typename T>
1131 inline const T *Memory<T>::Read(MemoryClass mc, int size) const
1132 {
1133  const size_t bytes = size * sizeof(T);
1134  if (!(flags & Registered))
1135  {
1136  if (mc == MemoryClass::HOST) { return h_ptr; }
1137  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1138  flags & OWNS_HOST, flags & ALIAS, flags);
1139  }
1140  return (const T*)MemoryManager::Read_(h_ptr, h_mt, mc, bytes, flags);
1141 }
1142 
1143 template <typename T>
1144 inline T *Memory<T>::Write(MemoryClass mc, int size)
1145 {
1146  const size_t bytes = size * sizeof(T);
1147  if (!(flags & Registered))
1148  {
1149  if (mc == MemoryClass::HOST) { return h_ptr; }
1150  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1151  flags & OWNS_HOST, flags & ALIAS, flags);
1152  }
1153  return (T*)MemoryManager::Write_(h_ptr, h_mt, mc, bytes, flags);
1154 }
1155 
1156 template <typename T>
1157 inline void Memory<T>::Sync(const Memory &other) const
1158 {
1159  if (!(flags & Registered) && (other.flags & Registered))
1160  {
1161  MFEM_ASSERT(h_ptr == other.h_ptr &&
1162  (flags & ALIAS) == (other.flags & ALIAS),
1163  "invalid input");
1165  }
1166  flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
1167  (other.flags & (VALID_HOST | VALID_DEVICE));
1168 }
1169 
1170 template <typename T>
1171 inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
1172 {
1173  // Assuming that if *this is registered then base is also registered.
1174  MFEM_ASSERT(!(flags & Registered) || (base.flags & Registered),
1175  "invalid base state");
1176  if (!(base.flags & Registered)) { return; }
1177  MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
1178  base.flags, flags);
1179 }
1180 
1181 template <typename T>
1183 {
1184  if (h_ptr == nullptr || !(flags & VALID_DEVICE)) { return h_mt; }
1185  return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1186 }
1187 
1188 template <typename T>
1190 {
1191  if (!(flags & Registered)) { return MemoryType::DEFAULT; }
1192  return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1193 }
1194 
1195 template <typename T>
1196 inline bool Memory<T>::HostIsValid() const
1197 {
1198  return flags & VALID_HOST ? true : false;
1199 }
1200 
1201 template <typename T>
1202 inline bool Memory<T>::DeviceIsValid() const
1203 {
1204  return flags & VALID_DEVICE ? true : false;
1205 }
1206 
1207 template <typename T>
1208 inline void Memory<T>::CopyFrom(const Memory &src, int size)
1209 {
1210  MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
1211  if (!(flags & Registered) && !(src.flags & Registered))
1212  {
1213  if (h_ptr != src.h_ptr && size != 0)
1214  {
1215  MFEM_ASSERT(h_ptr + size <= src.h_ptr || src.h_ptr + size <= h_ptr,
1216  "data overlaps!");
1217  std::memcpy(h_ptr, src, size*sizeof(T));
1218  }
1219  // *this is not registered, so (flags & VALID_HOST) must be true
1220  }
1221  else
1222  {
1223  MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
1224  }
1225 }
1226 
1227 template <typename T>
1228 inline void Memory<T>::CopyFromHost(const T *src, int size)
1229 {
1230  MFEM_VERIFY(capacity>=size, "Incorrect size");
1231  if (!(flags & Registered))
1232  {
1233  if (h_ptr != src && size != 0)
1234  {
1235  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
1236  "data overlaps!");
1237  std::memcpy(h_ptr, src, size*sizeof(T));
1238  }
1239  // *this is not registered, so (flags & VALID_HOST) must be true
1240  }
1241  else
1242  {
1243  MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
1244  }
1245 }
1246 
1247 template <typename T>
1248 inline void Memory<T>::CopyTo(Memory &dest, int size) const
1249 {
1250  MFEM_VERIFY(capacity>=size, "Incorrect size");
1251  dest.CopyFrom(*this, size);
1252 }
1253 
1254 template <typename T>
1255 inline void Memory<T>::CopyToHost(T *dest, int size) const
1256 {
1257  MFEM_VERIFY(capacity>=size, "Incorrect size");
1258  if (!(flags & Registered))
1259  {
1260  if (h_ptr != dest && size != 0)
1261  {
1262  MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
1263  "data overlaps!");
1264  std::memcpy(dest, h_ptr, size*sizeof(T));
1265  }
1266  }
1267  else
1268  {
1269  MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
1270  }
1271 }
1272 
1273 
1274 /** @brief Print the state of a Memory object based on its internal flags.
1275  Useful in a debugger. See also Memory<T>::PrintFlags(). */
1276 extern void MemoryPrintFlags(unsigned flags);
1277 
1278 
1279 template <typename T>
1280 inline void Memory<T>::PrintFlags() const
1281 {
1283 }
1284 
1285 template <typename T>
1286 inline int Memory<T>::CompareHostAndDevice(int size) const
1287 {
1288  if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
1289  return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
1290 }
1291 
1292 
1293 /// The (single) global memory manager object
1294 extern MFEM_EXPORT MemoryManager mm;
1295 
1296 } // namespace mfem
1297 
1298 #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:75
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:60
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...