MFEM  v4.1.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
mem_manager.hpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2020, 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 
20 namespace mfem
21 {
22 
23 // Implementation of MFEM's lightweight device/host memory manager designed to
24 // work seamlessly with the OCCA, RAJA, and other kernels supported by MFEM.
25 
26 /// Memory types supported by MFEM.
27 enum class MemoryType
28 {
29  HOST, ///< Host memory; using new[] and delete[]
30  HOST_32, ///< Host memory; aligned at 32 bytes
31  HOST_64, ///< Host memory; aligned at 64 bytes
32  HOST_DEBUG, ///< Host memory; allocated from a "host-debug" pool
33  HOST_UMPIRE, ///< Host memory; using Umpire
34  MANAGED, /**< Managed memory; using CUDA or HIP *MallocManaged
35  and *Free */
36  DEVICE, ///< Device memory; using CUDA or HIP *Malloc and *Free
37  DEVICE_DEBUG, /**< Pseudo-device memory; allocated on host from a
38  "device-debug" pool */
39  DEVICE_UMPIRE, ///< Device memory; using Umpire
40  SIZE ///< Number of host and device memory types
41 };
42 
43 /// Static casts to 'int' and sizes of some useful memory types.
44 constexpr int MemoryTypeSize = static_cast<int>(MemoryType::SIZE);
45 constexpr int HostMemoryType = static_cast<int>(MemoryType::HOST);
46 constexpr int HostMemoryTypeSize = static_cast<int>(MemoryType::DEVICE);
47 constexpr int DeviceMemoryType = static_cast<int>(MemoryType::MANAGED);
49 
50 /// Memory type names, used during Device:: configuration.
51 extern const char *MemoryTypeName[MemoryTypeSize];
52 
53 /// Memory classes identify sets of memory types.
54 /** This type is used by kernels that can work with multiple MemoryType%s.
55  * For example, kernels that can use DEVICE or MANAGED memory types should
56  * use MemoryClass::DEVICE for their inputs. */
57 enum class MemoryClass
58 {
59  HOST, /**< Memory types: { HOST, HOST_32, HOST_64, HOST_DEBUG,
60  HOST_UMPIRE, MANAGED } */
61  HOST_32, ///< Memory types: { HOST_32, HOST_64, HOST_DEBUG }
62  HOST_64, ///< Memory types: { HOST_64, HOST_DEBUG }
63  DEVICE, ///< Memory types: { DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE, MANAGED }
64  MANAGED ///< Memory types: { MANAGED }
65 };
66 
67 /// Return true if the given memory type is in MemoryClass::HOST.
68 inline bool IsHostMemory(MemoryType mt) { return mt <= MemoryType::MANAGED; }
69 inline bool IsDeviceMemory(MemoryType mt) { return mt >= MemoryType::MANAGED; }
70 
71 /// Return a suitable MemoryType for a given MemoryClass.
73 
74 /// Return a suitable MemoryClass from a pair of MemoryClass%es.
75 /** Note: this operation is commutative, i.e. a*b = b*a, associative, i.e.
76  (a*b)*c = a*(b*c), and has an identity element: MemoryClass::HOST.
77 
78  Currently, the operation is defined as a*b := max(a,b) where the max
79  operation is based on the enumeration ordering:
80 
81  HOST < HOST_32 < HOST_64 < DEVICE < MANAGED. */
83 
84 /// Class used by MFEM to store pointers to host and/or device memory.
85 /** The template class parameter, T, must be a plain-old-data (POD) type.
86 
87  In many respects this class behaves like a pointer:
88  * When destroyed, a Memory object does NOT automatically delete any
89  allocated memory.
90  * Only the method Delete() will deallocate a Memory object.
91  * Other methods that modify the object (e.g. New(), Wrap(), etc) will simply
92  overwrite the old contents.
93  * One difference with a pointer is that a const Memory object does not allow
94  modification of the content (unlike e.g. a const pointer).
95 
96  A Memory object stores up to two different pointers: one host pointer (with
97  MemoryType from MemoryClass::HOST) and one device pointer (currently one of
98  MemoryType: DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE or MANAGED).
99 
100  A Memory object can hold (wrap) an externally allocated pointer with any
101  given MemoryType.
102 
103  Access to the content of the Memory object can be requested with any given
104  MemoryClass through the methods ReadWrite(), Read(), and Write().
105  Requesting such access may result in additional (internally handled)
106  memory allocation and/or memory copy.
107  * When ReadWrite() is called, the returned pointer becomes the only
108  valid pointer.
109  * When Read() is called, the returned pointer becomes valid, however
110  the other pointer (host or device) may remain valid as well.
111  * When Write() is called, the returned pointer becomes the only valid
112  pointer, however, unlike ReadWrite(), no memory copy will be performed.
113 
114  The host memory (pointer from MemoryClass::HOST) can be accessed through the
115  inline methods: `operator[]()`, `operator*()`, the implicit conversion
116  functions `operator T*()`, `operator const T*()`, and the explicit
117  conversion template functions `operator U*()`, `operator const U*()` (with
118  any suitable type U). In certain cases, using these methods may have
119  undefined behavior, e.g. if the host pointer is not currently valid. */
120 template <typename T>
121 class Memory
122 {
123 protected:
124  friend class MemoryManager;
125  friend void MemoryPrintFlags(unsigned flags);
126 
127  enum FlagMask: unsigned
128  {
129  REGISTERED = 1 << 0, /**< The host pointer is registered with the
130  MemoryManager */
131  OWNS_HOST = 1 << 1, ///< The host pointer will be deleted by Delete()
132  OWNS_DEVICE = 1 << 2, /**< The device pointer will be deleted by
133  Delete() */
134  OWNS_INTERNAL = 1 << 3, ///< Ownership flag for internal Memory data
135  VALID_HOST = 1 << 4, ///< Host pointer is valid
136  VALID_DEVICE = 1 << 5, ///< %Device pointer is valid
137  USE_DEVICE = 1 << 6, /**< Internal device flag, see e.g.
138  Vector::UseDevice() */
139  ALIAS = 1 << 7 ///< Pointer is an alias
140  };
141 
142  /// Pointer to host memory. Not owned.
143  /** The type of the pointer is given by the field #h_mt; it can be any type
144  from MemoryClass::HOST. */
145  T *h_ptr;
146  int capacity; ///< Size of the allocated memory
147  MemoryType h_mt; ///< Host memory type
148  mutable unsigned flags; ///< Bit flags defined from the #FlagMask enum
149  // 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
150  // Copy{From,To}, {ReadWrite,Read,Write}.
151 
152 public:
153  /// Default constructor: no initialization.
154  Memory() { }
155 
156  /// Copy constructor: default.
157  Memory(const Memory &orig) = default;
158 
159  /// Move constructor: default.
160  Memory(Memory &&orig) = default;
161 
162  /// Copy-assignment operator: default.
163  Memory &operator=(const Memory &orig) = default;
164 
165  /// Move-assignment operator: default.
166  Memory &operator=(Memory &&orig) = default;
167 
168  /// Allocate host memory for @a size entries.
169  /** The allocation uses the current host memory type returned by
170  MemoryManager::GetHostMemoryType(). */
171  explicit Memory(int size) { New(size); }
172 
173  /** @brief Allocate memory for @a size entries with the given MemoryType
174  @a mt. */
175  /** The newly allocated memory is not initialized, however the given
176  MemoryType is still set as valid. */
177  Memory(int size, MemoryType mt) { New(size, mt); }
178 
179  /** @brief Wrap an externally allocated host pointer, @a ptr with the current
180  host memory type returned by MemoryManager::GetHostMemoryType(). */
181  /** The parameter @a own determines whether @a ptr will be deleted when the
182  method Delete() is called. */
183  explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
184 
185  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
186  /** The new memory object will have the given MemoryType set as valid.
187 
188  The given @a ptr must be allocated appropriately for the given
189  MemoryType.
190 
191  The parameter @a own determines whether @a ptr will be deleted when the
192  method Delete() is called. */
193  Memory(T *ptr, int size, MemoryType mt, bool own)
194  { Wrap(ptr, size, mt, own); }
195 
196  /** @brief Alias constructor. Create a Memory object that points inside the
197  Memory object @a base. */
198  /** The new Memory object uses the same MemoryType(s) as @a base. */
199  Memory(const Memory &base, int offset, int size)
200  { MakeAlias(base, offset, size); }
201 
202  /// Destructor: default.
203  /** @note The destructor will NOT delete the current memory. */
204  ~Memory() = default;
205 
206  /** @brief Return true if the host pointer is owned. Ownership indicates
207  whether the pointer will be deleted by the method Delete(). */
208  bool OwnsHostPtr() const { return flags & OWNS_HOST; }
209 
210  /** @brief Set/clear the ownership flag for the host pointer. Ownership
211  indicates whether the pointer will be deleted by the method Delete(). */
212  void SetHostPtrOwner(bool own) const
213  { flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
214 
215  /** @brief Return true if the device pointer is owned. Ownership indicates
216  whether the pointer will be deleted by the method Delete(). */
217  bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
218 
219  /** @brief Set/clear the ownership flag for the device pointer. Ownership
220  indicates whether the pointer will be deleted by the method Delete(). */
221  void SetDevicePtrOwner(bool own) const
222  { flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
223 
224  /** @brief Clear the ownership flags for the host and device pointers, as
225  well as any internal data allocated by the Memory object. */
226  void ClearOwnerFlags() const
228 
229  /// Read the internal device flag.
230  bool UseDevice() const { return flags & USE_DEVICE; }
231 
232  /// Set the internal device flag.
233  void UseDevice(bool use_dev) const
234  { flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
235 
236  /// Return the size of the allocated memory.
237  int Capacity() const { return capacity; }
238 
239  /// Reset the memory to be empty, ensuring that Delete() will be a no-op.
240  /** This is the Memory class equivalent to setting a pointer to NULL, see
241  Empty().
242 
243  @note The current memory is NOT deleted by this method. */
244  void Reset();
245 
246  /// Reset the memory and set the host memory type.
247  void Reset(MemoryType host_mt);
248 
249  /// Return true if the Memory object is empty, see Reset().
250  /** Default-constructed objects are uninitialized, so they are not guaranteed
251  to be empty. */
252  bool Empty() const { return h_ptr == NULL; }
253 
254  /** @brief Allocate host memory for @a size entries with the current host
255  memory type returned by MemoryManager::GetHostMemoryType(). */
256  /** @note The current memory is NOT deleted by this method. */
257  inline void New(int size);
258 
259  /// Allocate memory for @a size entries with the given MemoryType.
260  /** The newly allocated memory is not initialized, however the given
261  MemoryType is still set as valid.
262 
263  @note The current memory is NOT deleted by this method. */
264  inline void New(int size, MemoryType mt);
265 
266  /** @brief Wrap an externally allocated host pointer, @a ptr with the current
267  host memory type returned by MemoryManager::GetHostMemoryType(). */
268  /** The parameter @a own determines whether @a ptr will be deleted when the
269  method Delete() is called.
270 
271  @note The current memory is NOT deleted by this method. */
272  inline void Wrap(T *ptr, int size, bool own);
273 
274  /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
275  /** The new memory object will have the given MemoryType set as valid.
276 
277  The given @a ptr must be allocated appropriately for the given
278  MemoryType.
279 
280  The parameter @a own determines whether @a ptr will be deleted when the
281  method Delete() is called.
282 
283  @note The current memory is NOT deleted by this method. */
284  inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
285 
286  /// Create a memory object that points inside the memory object @a base.
287  /** The new Memory object uses the same MemoryType(s) as @a base.
288 
289  @note The current memory is NOT deleted by this method. */
290  inline void MakeAlias(const Memory &base, int offset, int size);
291 
292  /** @brief Delete the owned pointers. The Memory is not reset by this method,
293  i.e. it will, generally, not be Empty() after this call. */
294  inline void Delete();
295 
296  /// Array subscript operator for host memory.
297  inline T &operator[](int idx);
298 
299  /// Array subscript operator for host memory, const version.
300  inline const T &operator[](int idx) const;
301 
302  /// Direct access to the host memory as T* (implicit conversion).
303  /** When the type T is const-qualified, this method can be used only if the
304  host pointer is currently valid (the device pointer may be valid or
305  invalid).
306 
307  When the type T is not const-qualified, this method can be used only if
308  the host pointer is the only valid pointer.
309 
310  When the Memory is empty, this method can be used and it returns NULL. */
311  inline operator T*();
312 
313  /// Direct access to the host memory as const T* (implicit conversion).
314  /** This method can be used only if the host pointer is currently valid (the
315  device pointer may be valid or invalid).
316 
317  When the Memory is empty, this method can be used and it returns NULL. */
318  inline operator const T*() const;
319 
320  /// Direct access to the host memory via explicit typecast.
321  /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
322  In particular, this method cannot be used to cast away const-ness from
323  the base type T.
324 
325  When the type U is const-qualified, this method can be used only if the
326  host pointer is currently valid (the device pointer may be valid or
327  invalid).
328 
329  When the type U is not const-qualified, this method can be used only if
330  the host pointer is the only valid pointer.
331 
332  When the Memory is empty, this method can be used and it returns NULL. */
333  template <typename U>
334  inline explicit operator U*();
335 
336  /// Direct access to the host memory via explicit typecast, const version.
337  /** A pointer to type T must be reinterpret_cast-able to a pointer to type
338  const U.
339 
340  This method can be used only if the host pointer is currently valid (the
341  device pointer may be valid or invalid).
342 
343  When the Memory is empty, this method can be used and it returns NULL. */
344  template <typename U>
345  inline explicit operator const U*() const;
346 
347  /// Get read-write access to the memory with the given MemoryClass.
348  /** If only read or only write access is needed, then the methods
349  Read() or Write() should be used instead of this method.
350 
351  The parameter @a size must not exceed the Capacity(). */
352  inline T *ReadWrite(MemoryClass mc, int size);
353 
354  /// Get read-only access to the memory with the given MemoryClass.
355  /** The parameter @a size must not exceed the Capacity(). */
356  inline const T *Read(MemoryClass mc, int size) const;
357 
358  /// Get write-only access to the memory with the given MemoryClass.
359  /** The parameter @a size must not exceed the Capacity().
360 
361  The contents of the returned pointer is undefined, unless it was
362  validated by a previous call to Read() or ReadWrite() with
363  the same MemoryClass. */
364  inline T *Write(MemoryClass mc, int size);
365 
366  /// Copy the host/device pointer validity flags from @a other to @a *this.
367  /** This method synchronizes the pointer validity flags of two Memory objects
368  that use the same host/device pointers, or when @a *this is an alias
369  (sub-Memory) of @a other. Typically, this method should be called after
370  @a other is manipulated in a way that changes its pointer validity flags
371  (e.g. it was moved from device to host memory). */
372  inline void Sync(const Memory &other) const;
373 
374  /** @brief Update the alias Memory @a *this to match the memory location (all
375  valid locations) of its base Memory, @a base. */
376  /** This method is useful when alias Memory is moved and manipulated in a
377  different memory space. Such operations render the pointer validity flags
378  of the base incorrect. Calling this method will ensure that @a base is
379  up-to-date. Note that this is achieved by moving/copying @a *this (if
380  necessary), and not @a base. */
381  inline void SyncAlias(const Memory &base, int alias_size) const;
382 
383  /** @brief Return a MemoryType that is currently valid. If both the host and
384  the device pointers are currently valid, then the device memory type is
385  returned. */
386  inline MemoryType GetMemoryType() const;
387 
388  /// Copy @a size entries from @a src to @a *this.
389  /** The given @a size should not exceed the Capacity() of the source @a src
390  and the destination, @a *this. */
391  inline void CopyFrom(const Memory &src, int size);
392 
393  /// Copy @a size entries from the host pointer @a src to @a *this.
394  /** The given @a size should not exceed the Capacity() of @a *this. */
395  inline void CopyFromHost(const T *src, int size);
396 
397  /// Copy @a size entries from @a *this to @a dest.
398  /** The given @a size should not exceed the Capacity() of @a *this and the
399  destination, @a dest. */
400  inline void CopyTo(Memory &dest, int size) const
401  { dest.CopyFrom(*this, size); }
402 
403  /// Copy @a size entries from @a *this to the host pointer @a dest.
404  /** The given @a size should not exceed the Capacity() of @a *this. */
405  inline void CopyToHost(T *dest, int size) const;
406 
407  /// Print the internal flags.
408  /** This method can be useful for debugging. It is explicitly instantiated
409  for Memory<T> with T = int and T = double. */
410  inline void PrintFlags() const;
411 
412  /// If both the host and the device data are valid, compare their contents.
413  /** This method can be useful for debugging. It is explicitly instantiated
414  for Memory<T> with T = int and T = double. */
415  inline int CompareHostAndDevice(int size) const;
416 };
417 
418 
419 /// The memory manager class
421 {
422 private:
423 
424  typedef MemoryType MemType;
425  typedef Memory<int> Mem;
426 
427  template <typename T> friend class Memory;
428 
429  /// Host memory type set during the Setup.
430  static MemoryType host_mem_type;
431 
432  /// Device memory type set during the Setup.
433  static MemoryType device_mem_type;
434 
435  /// Allow to detect if a global memory manager instance exists.
436  static bool exists;
437 
438  /// Return true if the global memory manager instance exists.
439  static bool Exists() { return exists; }
440 
441  /// Host and device allocator names for Umpire.
442 #ifdef MFEM_USE_UMPIRE
443  static const char *h_umpire_name;
444  static const char *d_umpire_name;
445 #endif
446 
447 private: // Static methods used by the Memory<T> class
448 
449  /// Allocate and register a new pointer. Return the host pointer.
450  /// h_tmp must be already allocated using new T[] if mt is a pure device
451  /// memory type, e.g. CUDA (mt will not be HOST).
452  static void *New_(void *h_tmp, size_t bytes, MemoryType mt, unsigned &flags);
453 
454  /// Register an external pointer of the given MemoryType.
455  /// Return the host pointer.
456  static void *Register_(void *ptr, void *h_ptr, size_t bytes, MemoryType mt,
457  bool own, bool alias, unsigned &flags);
458 
459  /// Register an alias. Note: base_h_ptr may be an alias.
460  static void Alias_(void *base_h_ptr, size_t offset, size_t bytes,
461  unsigned base_flags, unsigned &flags);
462 
463  /// Un-register and free memory identified by its host pointer. Returns the
464  /// memory type of the host pointer.
465  static MemoryType Delete_(void *h_ptr, MemoryType mt, unsigned flags);
466 
467  /// Check if the memory types given the memory class are valid
468  static bool MemoryClassCheck_(MemoryClass mc, void *h_ptr,
469  MemoryType h_mt, size_t bytes, unsigned flags);
470 
471  /// Return the dual memory type of the given one.
472  static MemoryType GetDualMemoryType_(MemoryType mt);
473 
474  /// Return a pointer to the memory identified by the host pointer h_ptr for
475  /// access with the given MemoryClass.
476  static void *ReadWrite_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
477  size_t bytes, unsigned &flags);
478 
479  static const void *Read_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
480  size_t bytes, unsigned &flags);
481 
482  static void *Write_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
483  size_t bytes, unsigned &flags);
484 
485  static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
486  size_t alias_bytes, unsigned base_flags,
487  unsigned &alias_flags);
488 
489  /// Return the type the of the currently valid memory.
490  /// If more than one types are valid, return a device type.
491  static MemoryType GetDeviceMemoryType_(void *h_ptr);
492 
493  /// Return the type the of the host memory.
494  static MemoryType GetHostMemoryType_(void *h_ptr);
495 
496  /// Verify that h_mt and h_ptr's h_mt (memory or alias) are equal.
497  static void CheckHostMemoryType_(MemoryType h_mt, void *h_ptr);
498 
499  /// Copy entries from valid memory type to valid memory type.
500  /// Both dest_h_ptr and src_h_ptr are registered host pointers.
501  static void Copy_(void *dest_h_ptr, const void *src_h_ptr, size_t bytes,
502  unsigned src_flags, unsigned &dest_flags);
503 
504  /// Copy entries from valid memory type to host memory, where dest_h_ptr is
505  /// not a registered host pointer and src_h_ptr is a registered host pointer.
506  static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
507  size_t bytes, unsigned src_flags);
508 
509  /// Copy entries from host memory to valid memory type, where dest_h_ptr is a
510  /// registered host pointer and src_h_ptr is not a registered host pointer.
511  static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
512  size_t bytes, unsigned &dest_flags);
513 
514  /// Check if the host pointer has been registered in the memory manager.
515  static bool IsKnown_(const void *h_ptr);
516 
517  /** @brief Check if the host pointer has been registered as an alias in the
518  memory manager. */
519  static bool IsAlias_(const void *h_ptr);
520 
521  /// Compare the contents of the host and the device memory.
522  static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
523 
524 private:
525 
526  /// Insert a host address in the memory map
527  void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
528 
529  /// Insert a device and the host addresses in the memory map
530  void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
531  MemoryType h_mt, MemoryType d_mt);
532 
533  /// Insert an alias in the alias map
534  void InsertAlias(const void *base_ptr, void *alias_ptr,
535  const size_t bytes, const bool base_is_alias);
536 
537  /// Erase an address from the memory map, as well as all its aliases
538  void Erase(void *h_ptr, bool free_dev_ptr = true);
539 
540  /// Erase an alias from the aliases map
541  void EraseAlias(void *alias_ptr);
542 
543  /// Return the corresponding device pointer of h_ptr,
544  /// allocating and moving the data if needed
545  void *GetDevicePtr(const void *h_ptr, size_t bytes, bool copy_data);
546 
547  /// Return the corresponding device pointer of alias_ptr,
548  /// allocating and moving the data if needed
549  void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
550 
551  /// Return the corresponding host pointer of d_ptr,
552  /// allocating and moving the data if needed
553  void *GetHostPtr(const void *d_ptr, size_t bytes, bool copy_data);
554 
555  /// Return the corresponding host pointer of alias_ptr,
556  /// allocating and moving the data if needed
557  void *GetAliasHostPtr(const void *alias_ptr, size_t bytes, bool copy_data);
558 
559 public:
560  MemoryManager();
561  ~MemoryManager();
562 
563  /// Initialize the memory manager.
564  void Init();
565 
566  /// Configure the Memory manager with given default host and device types
567  /// This method will be called when configuring a device.
568  void Configure(const MemoryType h_mt, const MemoryType d_mt);
569 
570 #ifdef MFEM_USE_UMPIRE
571  /// Set the host and device UMpire allocator names
572  void SetUmpireAllocatorNames(const char *h_name, const char *d_name);
573  const char *GetUmpireAllocatorHostName() { return h_umpire_name; }
574  const char *GetUmpireAllocatorDeviceName() { return d_umpire_name; }
575 #endif
576 
577  /// Free all the device memories
578  void Destroy();
579 
580  /// Return true if the pointer is known by the memory manager
581  bool IsKnown(const void *h_ptr) { return IsKnown_(h_ptr); }
582 
583  /// Return true if the pointer is known by the memory manager as an alias
584  bool IsAlias(const void *h_ptr) { return IsAlias_(h_ptr); }
585 
586  /// Check if the host pointer has been registered in the memory manager
587  void RegisterCheck(void *h_ptr);
588 
589  /// Prints all pointers known by the memory manager,
590  /// returning the number of printed pointers
591  int PrintPtrs(std::ostream &out = mfem::out);
592 
593  /// Prints all aliases known by the memory manager
594  /// returning the number of printed pointers
595  int PrintAliases(std::ostream &out = mfem::out);
596 
597  static MemoryType GetHostMemoryType() { return host_mem_type; }
598  static MemoryType GetDeviceMemoryType() { return device_mem_type; }
599 };
600 
601 
602 // Inline methods
603 
604 template <typename T>
605 inline void Memory<T>::Reset()
606 {
607  h_ptr = NULL;
608  h_mt = MemoryManager::host_mem_type;
609  capacity = 0;
610  flags = 0;
611 }
612 
613 template <typename T>
614 inline void Memory<T>::Reset(MemoryType host_mt)
615 {
616  h_ptr = NULL;
617  h_mt = host_mt;
618  capacity = 0;
619  flags = 0;
620 }
621 
622 template <typename T>
623 inline void Memory<T>::New(int size)
624 {
625  capacity = size;
626  flags = OWNS_HOST | VALID_HOST;
627  h_mt = MemoryManager::host_mem_type;
628  h_ptr = (h_mt == MemoryType::HOST) ? new T[size] :
629  (T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
630 }
631 
632 template <typename T>
633 inline void Memory<T>::New(int size, MemoryType mt)
634 {
635  capacity = size;
636  const size_t bytes = size*sizeof(T);
637  const bool mt_host = mt == MemoryType::HOST;
638  if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
639  h_mt = IsHostMemory(mt) ? mt : MemoryManager::GetDualMemoryType_(mt);
640  T *h_tmp = (h_mt == MemoryType::HOST) ? new T[size] : nullptr;
641  h_ptr = (mt_host) ? h_tmp: (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
642 }
643 
644 template <typename T>
645 inline void Memory<T>::Wrap(T *ptr, int size, bool own)
646 {
647  h_ptr = ptr;
648  capacity = size;
649  const size_t bytes = size*sizeof(T);
650  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
651  h_mt = MemoryManager::host_mem_type;
652 #ifdef MFEM_DEBUG
653  if (own && MemoryManager::Exists())
654  { MFEM_VERIFY(h_mt == MemoryManager::GetHostMemoryType_(h_ptr),""); }
655 #endif
656  if (own && h_mt != MemoryType::HOST)
657  { MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags); }
658 }
659 
660 template <typename T>
661 inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
662 {
663  capacity = size;
664  if (IsHostMemory(mt))
665  {
666  h_mt = mt;
667  h_ptr = ptr;
668  if (mt == MemoryType::HOST || !own)
669  {
670  // Skip restration
671  flags = (own ? OWNS_HOST : 0) | VALID_HOST;
672  return;
673  }
674  }
675  else
676  {
677  h_mt = MemoryManager::GetDualMemoryType_(mt);
678  h_ptr = (h_mt == MemoryType::HOST) ? new T[size] : nullptr;
679  }
680  flags = 0;
681  h_ptr = (T*)MemoryManager::Register_(ptr, h_ptr, size*sizeof(T), mt,
682  own, false, flags);
683 }
684 
685 template <typename T>
686 inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
687 {
688  capacity = size;
689  h_mt = base.h_mt;
690  h_ptr = base.h_ptr + offset;
691  if (!(base.flags & REGISTERED))
692  { flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE); }
693  else
694  {
695  const size_t s_bytes = size*sizeof(T);
696  const size_t o_bytes = offset*sizeof(T);
697  MemoryManager::Alias_(base.h_ptr, o_bytes, s_bytes, base.flags, flags);
698  }
699 }
700 
701 template <typename T>
702 inline void Memory<T>::Delete()
703 {
704  const bool registered = flags & REGISTERED;
705  const bool mt_host = h_mt == MemoryType::HOST;
706  const bool std_delete = !registered && mt_host;
707 
708  if (std_delete ||
709  MemoryManager::Delete_((void*)h_ptr, h_mt, flags) == MemoryType::HOST)
710  {
711  if (flags & OWNS_HOST) { delete [] h_ptr; }
712  }
713 }
714 
715 template <typename T>
716 inline T &Memory<T>::operator[](int idx)
717 {
718  MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
719  "invalid host pointer access");
720  return h_ptr[idx];
721 }
722 
723 template <typename T>
724 inline const T &Memory<T>::operator[](int idx) const
725 {
726  MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
727  return h_ptr[idx];
728 }
729 
730 template <typename T>
732 {
733  MFEM_ASSERT(Empty() ||
734  ((flags & VALID_HOST) &&
735  (std::is_const<T>::value || !(flags & VALID_DEVICE))),
736  "invalid host pointer access");
737  return h_ptr;
738 }
739 
740 template <typename T>
741 inline Memory<T>::operator const T*() const
742 {
743  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
744  return h_ptr;
745 }
746 
747 template <typename T> template <typename U>
749 {
750  MFEM_ASSERT(Empty() ||
751  ((flags & VALID_HOST) &&
752  (std::is_const<U>::value || !(flags & VALID_DEVICE))),
753  "invalid host pointer access");
754  return reinterpret_cast<U*>(h_ptr);
755 }
756 
757 template <typename T> template <typename U>
758 inline Memory<T>::operator const U*() const
759 {
760  MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
761  return reinterpret_cast<U*>(h_ptr);
762 }
763 
764 template <typename T>
765 inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
766 {
767  const size_t bytes = size * sizeof(T);
768  if (!(flags & REGISTERED))
769  {
770  if (mc == MemoryClass::HOST) { return h_ptr; }
771  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
772  flags & OWNS_HOST, flags & ALIAS, flags);
773  }
774  return (T*)MemoryManager::ReadWrite_(h_ptr, h_mt, mc, bytes, flags);
775 }
776 
777 template <typename T>
778 inline const T *Memory<T>::Read(MemoryClass mc, int size) const
779 {
780  const size_t bytes = size * sizeof(T);
781  if (!(flags & REGISTERED))
782  {
783  if (mc == MemoryClass::HOST) { return h_ptr; }
784  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
785  flags & OWNS_HOST, flags & ALIAS, flags);
786  }
787  return (const T*)MemoryManager::Read_(h_ptr, h_mt, mc, bytes, flags);
788 }
789 
790 template <typename T>
791 inline T *Memory<T>::Write(MemoryClass mc, int size)
792 {
793  const size_t bytes = size * sizeof(T);
794  if (!(flags & REGISTERED))
795  {
796  if (mc == MemoryClass::HOST) { return h_ptr; }
797  MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
798  flags & OWNS_HOST, flags & ALIAS, flags);
799  }
800  return (T*)MemoryManager::Write_(h_ptr, h_mt, mc, bytes, flags);
801 }
802 
803 template <typename T>
804 inline void Memory<T>::Sync(const Memory &other) const
805 {
806  if (!(flags & REGISTERED) && (other.flags & REGISTERED))
807  {
808  MFEM_ASSERT(h_ptr == other.h_ptr &&
809  (flags & ALIAS) == (other.flags & ALIAS),
810  "invalid input");
811  flags = (flags | REGISTERED) & ~(OWNS_DEVICE | OWNS_INTERNAL);
812  }
813  flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
814  (other.flags & (VALID_HOST | VALID_DEVICE));
815 }
816 
817 template <typename T>
818 inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
819 {
820  // Assuming that if *this is registered then base is also registered.
821  MFEM_ASSERT(!(flags & REGISTERED) || (base.flags & REGISTERED),
822  "invalid base state");
823  if (!(base.flags & REGISTERED)) { return; }
824  MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
825  base.flags, flags);
826 }
827 
828 template <typename T>
830 {
831  if (!(flags & VALID_DEVICE)) { return h_mt; }
832  return MemoryManager::GetDeviceMemoryType_(h_ptr);
833 }
834 
835 template <typename T>
836 inline void Memory<T>::CopyFrom(const Memory &src, int size)
837 {
838  if (!(flags & REGISTERED) && !(src.flags & REGISTERED))
839  {
840  if (h_ptr != src.h_ptr && size != 0)
841  {
842  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
843  "data overlaps!");
844  std::memcpy(h_ptr, src, size*sizeof(T));
845  }
846  // *this is not registered, so (flags & VALID_HOST) must be true
847  }
848  else
849  {
850  MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
851  }
852 }
853 
854 template <typename T>
855 inline void Memory<T>::CopyFromHost(const T *src, int size)
856 {
857  if (!(flags & REGISTERED))
858  {
859  if (h_ptr != src && size != 0)
860  {
861  MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
862  "data overlaps!");
863  std::memcpy(h_ptr, src, size*sizeof(T));
864  }
865  // *this is not registered, so (flags & VALID_HOST) must be true
866  }
867  else
868  {
869  MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
870  }
871 }
872 
873 template <typename T>
874 inline void Memory<T>::CopyToHost(T *dest, int size) const
875 {
876  if (!(flags & REGISTERED))
877  {
878  if (h_ptr != dest && size != 0)
879  {
880  MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
881  "data overlaps!");
882  std::memcpy(dest, h_ptr, size*sizeof(T));
883  }
884  }
885  else
886  {
887  MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
888  }
889 }
890 
891 
892 /** @brief Print the state of a Memory object based on its internal flags.
893  Useful in a debugger. See also Memory<T>::PrintFlags(). */
894 extern void MemoryPrintFlags(unsigned flags);
895 
896 
897 template <typename T>
898 inline void Memory<T>::PrintFlags() const
899 {
900  MemoryPrintFlags(flags);
901 }
902 
903 template <typename T>
904 inline int Memory<T>::CompareHostAndDevice(int size) const
905 {
906  if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
907  return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
908 }
909 
910 
911 /// The (single) global memory manager object
912 extern MemoryManager mm;
913 
914 } // namespace mfem
915 
916 #endif // MFEM_MEM_MANAGER_HPP
MemoryType h_mt
Host memory type.
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:68
~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&lt;T&gt;::PrintFlags().
Device memory; using CUDA or HIP *Malloc and *Free.
const char * GetUmpireAllocatorHostName()
void PrintFlags() const
Print the internal flags.
Device memory; using Umpire.
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
void Delete()
Delete the owned pointers. The Memory is not reset by this method, i.e. it will, generally, not be Empty() after this call.
const char * GetUmpireAllocatorDeviceName()
Memory(T *ptr, int size, MemoryType mt, bool own)
Wrap an externally allocated pointer, ptr, of the given MemoryType.
static MemoryType GetHostMemoryType()
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...
Host pointer is valid.
void SetDevicePtrOwner(bool own) const
Set/clear the ownership flag for the device pointer. Ownership indicates whether the pointer will be ...
Host memory; allocated from a &quot;host-debug&quot; pool.
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
void Configure(const MemoryType h_mt, const MemoryType d_mt)
Memory(int size)
Allocate host memory for size entries.
void UseDevice(bool use_dev) const
Set the internal device flag.
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
int PrintAliases(std::ostream &out=mfem::out)
Number of host and device memory types.
int Capacity() const
Return the size of the allocated memory.
int CompareHostAndDevice(int size) const
If both the host and the device data are valid, compare their contents.
Memory(int size, MemoryType mt)
Allocate memory for size entries with the given MemoryType mt.
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
unsigned flags
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.
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...
void SetUmpireAllocatorNames(const char *h_name, const char *d_name)
Set the host and device UMpire allocator names.
void CopyFromHost(const T *src, int size)
Copy size entries from the host pointer src to *this.
void CopyTo(Memory &dest, int size) const
Copy size entries from *this to dest.
The memory manager class.
constexpr int DeviceMemoryType
Definition: mem_manager.hpp:47
constexpr int HostMemoryType
Definition: mem_manager.hpp:45
static MemoryType GetDeviceMemoryType()
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
void Destroy()
Free all the device memories.
int PrintPtrs(std::ostream &out=mfem::out)
The host pointer will be deleted by Delete()
bool OwnsHostPtr() const
Return true if the host pointer is owned. Ownership indicates whether the pointer will be deleted by ...
void RegisterCheck(void *h_ptr)
Check if the host pointer has been registered in the memory manager.
constexpr int MemoryTypeSize
Static casts to &#39;int&#39; and sizes of some useful memory types.
Definition: mem_manager.hpp:44
Memory & operator=(const Memory &orig)=default
Copy-assignment operator: default.
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
void Init()
Initialize the memory manager.
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:27
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
void Sync(const Memory &other) const
Copy the host/device pointer validity flags from other to *this.
bool IsKnown(const void *h_ptr)
Return true if the pointer is known by the memory manager.
constexpr int HostMemoryTypeSize
Definition: mem_manager.hpp:46
void MakeAlias(const Memory &base, int offset, int size)
Create a memory object that points inside the memory object base.
bool IsDeviceMemory(MemoryType mt)
Definition: mem_manager.hpp:69
Pointer is an alias.
MemoryManager mm
The (single) global memory manager object.
T * h_ptr
Pointer to host memory. Not owned.
int capacity
Size of the allocated memory.
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...
bool Empty() const
Return true if the Memory object is empty, see Reset().
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
Definition: mem_manager.cpp:49
void CopyToHost(T *dest, int size) const
Copy size entries from *this to the host pointer dest.
Class used by MFEM to store pointers to host and/or device memory.
Memory()
Default constructor: no initialization.
bool OwnsDevicePtr() const
Return true if the device pointer is owned. Ownership indicates whether the pointer will be deleted b...
bool UseDevice() const
Read the internal device flag.
constexpr int DeviceMemoryTypeSize
Definition: mem_manager.hpp:48
Memory(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
Host memory; using Umpire.
T & operator[](int idx)
Array subscript operator for host memory.
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
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
Return a suitable MemoryClass from a pair of MemoryClasses.
Definition: mem_manager.cpp:97
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
MemoryClass
Memory classes identify sets of memory types.
Definition: mem_manager.hpp:57
void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger. See also Memory&lt;T&gt;::PrintFlags().