MFEM  v4.3.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
mem_manager.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2021, 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 #include "forall.hpp"
13 #include "mem_manager.hpp"
14 
15 #include <list>
16 #include <cstring> // std::memcpy, std::memcmp
17 #include <unordered_map>
18 #include <algorithm> // std::max
19 
20 // Uncomment to try _WIN32 platform
21 //#define _WIN32
22 //#define _aligned_malloc(s,a) malloc(s)
23 
24 #ifndef _WIN32
25 #include <unistd.h>
26 #include <signal.h>
27 #include <sys/mman.h>
28 #define mfem_memalign(p,a,s) posix_memalign(p,a,s)
29 #define mfem_aligned_free free
30 #else
31 #define mfem_memalign(p,a,s) (((*(p))=_aligned_malloc((s),(a))),*(p)?0:errno)
32 #define mfem_aligned_free _aligned_free
33 #endif
34 
35 #ifdef MFEM_USE_UMPIRE
36 #include "umpire/Umpire.hpp"
37 
38 // Make sure Umpire is build with CUDA support if MFEM is built with it.
39 #if defined(MFEM_USE_CUDA) && !defined(UMPIRE_ENABLE_CUDA)
40 #error "CUDA is not enabled in Umpire!"
41 #endif
42 // Make sure Umpire is build with HIP support if MFEM is built with it.
43 #if defined(MFEM_USE_HIP) && !defined(UMPIRE_ENABLE_HIP)
44 #error "HIP is not enabled in Umpire!"
45 #endif
46 #endif // MFEM_USE_UMPIRE
47 
48 // Internal debug option, useful for tracking some memory manager operations.
49 // #define MFEM_TRACK_MEM_MANAGER
50 
51 namespace mfem
52 {
53 
55 {
56  switch (mc)
57  {
58  case MemoryClass::HOST: return mm.GetHostMemoryType();
63  }
64  MFEM_VERIFY(false,"");
65  return MemoryType::HOST;
66 }
67 
68 
70 {
71  switch (mc)
72  {
73  case MemoryClass::HOST: return IsHostMemory(mt);
75  return (mt == MemoryType::HOST_32 ||
76  mt == MemoryType::HOST_64 ||
79  return (mt == MemoryType::HOST_64 ||
81  case MemoryClass::DEVICE: return IsDeviceMemory(mt);
83  return (mt == MemoryType::MANAGED);
84  }
85  MFEM_ABORT("invalid MemoryClass");
86  return false;
87 }
88 
89 
90 static void MFEM_VERIFY_TYPES(const MemoryType h_mt, const MemoryType d_mt)
91 {
92  MFEM_VERIFY(IsHostMemory(h_mt), "h_mt = " << (int)h_mt);
93  MFEM_VERIFY(IsDeviceMemory(d_mt) || d_mt == MemoryType::DEFAULT,
94  "d_mt = " << (int)d_mt);
95  // If h_mt == MemoryType::HOST_DEBUG, then d_mt == MemoryType::DEVICE_DEBUG
96  // or d_mt == MemoryType::DEFAULT
97  MFEM_VERIFY(h_mt != MemoryType::HOST_DEBUG ||
98  d_mt == MemoryType::DEVICE_DEBUG ||
99  d_mt == MemoryType::DEFAULT,
100  "d_mt = " << MemoryTypeName[(int)d_mt]);
101  // If d_mt == MemoryType::DEVICE_DEBUG, then h_mt != MemoryType::MANAGED
102  MFEM_VERIFY(d_mt != MemoryType::DEVICE_DEBUG ||
103  h_mt != MemoryType::MANAGED,
104  "h_mt = " << MemoryTypeName[(int)h_mt]);
105 #if 0
106  const bool sync =
107  (h_mt == MemoryType::HOST_PINNED && d_mt == MemoryType::DEVICE) ||
108  (h_mt == MemoryType::HOST_PINNED && d_mt == MemoryType::DEVICE_UMPIRE) ||
110  (h_mt == MemoryType::HOST_UMPIRE && d_mt == MemoryType::DEVICE) ||
111  (h_mt == MemoryType::HOST_UMPIRE && d_mt == MemoryType::DEVICE_UMPIRE) ||
113  (h_mt == MemoryType::HOST_DEBUG && d_mt == MemoryType::DEVICE_DEBUG) ||
114  (h_mt == MemoryType::MANAGED && d_mt == MemoryType::MANAGED) ||
115  (h_mt == MemoryType::HOST_64 && d_mt == MemoryType::DEVICE) ||
116  (h_mt == MemoryType::HOST_32 && d_mt == MemoryType::DEVICE) ||
117  (h_mt == MemoryType::HOST && d_mt == MemoryType::DEVICE) ||
118  (h_mt == MemoryType::HOST && d_mt == MemoryType::DEVICE_UMPIRE) ||
119  (h_mt == MemoryType::HOST && d_mt == MemoryType::DEVICE_UMPIRE_2);
120  MFEM_VERIFY(sync, "");
121 #endif
122 }
123 
125 {
126  // | HOST HOST_32 HOST_64 DEVICE MANAGED
127  // ---------+---------------------------------------------
128  // HOST | HOST HOST_32 HOST_64 DEVICE MANAGED
129  // HOST_32 | HOST_32 HOST_32 HOST_64 DEVICE MANAGED
130  // HOST_64 | HOST_64 HOST_64 HOST_64 DEVICE MANAGED
131  // DEVICE | DEVICE DEVICE DEVICE DEVICE MANAGED
132  // MANAGED | MANAGED MANAGED MANAGED MANAGED MANAGED
133 
134  // Using the enumeration ordering:
135  // HOST < HOST_32 < HOST_64 < DEVICE < MANAGED,
136  // the above table is simply: a*b = max(a,b).
137 
138  return std::max(mc1, mc2);
139 }
140 
141 
142 // Instantiate Memory<T>::PrintFlags for T = int and T = double.
143 template void Memory<int>::PrintFlags() const;
144 template void Memory<double>::PrintFlags() const;
145 
146 // Instantiate Memory<T>::CompareHostAndDevice for T = int and T = double.
147 template int Memory<int>::CompareHostAndDevice(int size) const;
148 template int Memory<double>::CompareHostAndDevice(int size) const;
149 
150 
151 namespace internal
152 {
153 
154 /// Memory class that holds:
155 /// - the host and the device pointer
156 /// - the size in bytes of this memory region
157 /// - the host and device type of this memory region
158 struct Memory
159 {
160  void *const h_ptr;
161  void *d_ptr;
162  const size_t bytes;
163  const MemoryType h_mt;
164  MemoryType d_mt;
165  mutable bool h_rw, d_rw;
166  Memory(void *p, size_t b, MemoryType h, MemoryType d):
167  h_ptr(p), d_ptr(nullptr), bytes(b), h_mt(h), d_mt(d),
168  h_rw(true), d_rw(true) { }
169 };
170 
171 /// Alias class that holds the base memory region and the offset
172 struct Alias
173 {
174  Memory *mem;
175  size_t offset;
176  size_t counter;
177  // 'h_mt' is already stored in 'mem', however, we use this field for type
178  // checking since the alias may be dangling, i.e. 'mem' may be invalid.
179  MemoryType h_mt;
180 };
181 
182 /// Maps for the Memory and the Alias classes
183 typedef std::unordered_map<const void*, Memory> MemoryMap;
184 typedef std::unordered_map<const void*, Alias> AliasMap;
185 
186 struct Maps
187 {
188  MemoryMap memories;
189  AliasMap aliases;
190 };
191 
192 } // namespace mfem::internal
193 
194 static internal::Maps *maps;
195 
196 namespace internal
197 {
198 
199 /// The host memory space base abstract class
200 class HostMemorySpace
201 {
202 public:
203  virtual ~HostMemorySpace() { }
204  virtual void Alloc(void **ptr, size_t bytes) { *ptr = std::malloc(bytes); }
205  virtual void Dealloc(void *ptr) { std::free(ptr); }
206  virtual void Protect(const Memory&, size_t) { }
207  virtual void Unprotect(const Memory&, size_t) { }
208  virtual void AliasProtect(const void*, size_t) { }
209  virtual void AliasUnprotect(const void*, size_t) { }
210 };
211 
212 /// The device memory space base abstract class
213 class DeviceMemorySpace
214 {
215 public:
216  virtual ~DeviceMemorySpace() { }
217  virtual void Alloc(Memory &base) { base.d_ptr = std::malloc(base.bytes); }
218  virtual void Dealloc(Memory &base) { std::free(base.d_ptr); }
219  virtual void Protect(const Memory&) { }
220  virtual void Unprotect(const Memory&) { }
221  virtual void AliasProtect(const void*, size_t) { }
222  virtual void AliasUnprotect(const void*, size_t) { }
223  virtual void *HtoD(void *dst, const void *src, size_t bytes)
224  { return std::memcpy(dst, src, bytes); }
225  virtual void *DtoD(void *dst, const void *src, size_t bytes)
226  { return std::memcpy(dst, src, bytes); }
227  virtual void *DtoH(void *dst, const void *src, size_t bytes)
228  { return std::memcpy(dst, src, bytes); }
229 };
230 
231 /// The default std:: host memory space
232 class StdHostMemorySpace : public HostMemorySpace { };
233 
234 /// The No host memory space
235 struct NoHostMemorySpace : public HostMemorySpace
236 {
237  void Alloc(void**, const size_t) { mfem_error("! Host Alloc error"); }
238 };
239 
240 /// The aligned 32 host memory space
241 class Aligned32HostMemorySpace : public HostMemorySpace
242 {
243 public:
244  Aligned32HostMemorySpace(): HostMemorySpace() { }
245  void Alloc(void **ptr, size_t bytes)
246  { if (mfem_memalign(ptr, 32, bytes) != 0) { throw ::std::bad_alloc(); } }
247  void Dealloc(void *ptr) { mfem_aligned_free(ptr); }
248 };
249 
250 /// The aligned 64 host memory space
251 class Aligned64HostMemorySpace : public HostMemorySpace
252 {
253 public:
254  Aligned64HostMemorySpace(): HostMemorySpace() { }
255  void Alloc(void **ptr, size_t bytes)
256  { if (mfem_memalign(ptr, 64, bytes) != 0) { throw ::std::bad_alloc(); } }
257  void Dealloc(void *ptr) { mfem_aligned_free(ptr); }
258 };
259 
260 #ifndef _WIN32
261 static uintptr_t pagesize = 0;
262 static uintptr_t pagemask = 0;
263 
264 /// Returns the restricted base address of the DEBUG segment
265 inline const void *MmuAddrR(const void *ptr)
266 {
267  const uintptr_t addr = (uintptr_t) ptr;
268  return (addr & pagemask) ? (void*) ((addr + pagesize) & ~pagemask) : ptr;
269 }
270 
271 /// Returns the prolongated base address of the MMU segment
272 inline const void *MmuAddrP(const void *ptr)
273 {
274  const uintptr_t addr = (uintptr_t) ptr;
275  return (void*) (addr & ~pagemask);
276 }
277 
278 /// Compute the restricted length for the MMU segment
279 inline uintptr_t MmuLengthR(const void *ptr, const size_t bytes)
280 {
281  // a ---->A:| |:B<---- b
282  const uintptr_t a = (uintptr_t) ptr;
283  const uintptr_t A = (uintptr_t) MmuAddrR(ptr);
284  MFEM_ASSERT(a <= A, "");
285  const uintptr_t b = a + bytes;
286  const uintptr_t B = b & ~pagemask;
287  MFEM_ASSERT(B <= b, "");
288  const uintptr_t length = B > A ? B - A : 0;
289  MFEM_ASSERT(length % pagesize == 0,"");
290  return length;
291 }
292 
293 /// Compute the prolongated length for the MMU segment
294 inline uintptr_t MmuLengthP(const void *ptr, const size_t bytes)
295 {
296  // |:A<----a | | b---->B:|
297  const uintptr_t a = (uintptr_t) ptr;
298  const uintptr_t A = (uintptr_t) MmuAddrP(ptr);
299  MFEM_ASSERT(A <= a, "");
300  const uintptr_t b = a + bytes;
301  const uintptr_t B = b & pagemask ? (b + pagesize) & ~pagemask : b;
302  MFEM_ASSERT(b <= B, "");
303  MFEM_ASSERT(B >= A,"");
304  const uintptr_t length = B - A;
305  MFEM_ASSERT(length % pagesize == 0,"");
306  return length;
307 }
308 
309 /// The protected access error, used for the host
310 static void MmuError(int, siginfo_t *si, void*)
311 {
312  fflush(0);
313  char str[64];
314  const void *ptr = si->si_addr;
315  sprintf(str, "Error while accessing address %p!", ptr);
316  mfem::out << std::endl << "An illegal memory access was made!";
317  MFEM_ABORT(str);
318 }
319 
320 /// MMU initialization, setting SIGBUS & SIGSEGV signals to MmuError
321 static void MmuInit()
322 {
323  if (pagesize > 0) { return; }
324  struct sigaction sa;
325  sa.sa_flags = SA_SIGINFO;
326  sigemptyset(&sa.sa_mask);
327  sa.sa_sigaction = MmuError;
328  if (sigaction(SIGBUS, &sa, NULL) == -1) { mfem_error("SIGBUS"); }
329  if (sigaction(SIGSEGV, &sa, NULL) == -1) { mfem_error("SIGSEGV"); }
330  pagesize = (uintptr_t) sysconf(_SC_PAGE_SIZE);
331  MFEM_ASSERT(pagesize > 0, "pagesize must not be less than 1");
332  pagemask = pagesize - 1;
333 }
334 
335 /// MMU allocation, through ::mmap
336 inline void MmuAlloc(void **ptr, const size_t bytes)
337 {
338  const size_t length = bytes == 0 ? 8 : bytes;
339  const int prot = PROT_READ | PROT_WRITE;
340  const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
341  *ptr = ::mmap(NULL, length, prot, flags, -1, 0);
342  if (*ptr == MAP_FAILED) { throw ::std::bad_alloc(); }
343 }
344 
345 /// MMU deallocation, through ::munmap
346 inline void MmuDealloc(void *ptr, const size_t bytes)
347 {
348  const size_t length = bytes == 0 ? 8 : bytes;
349  if (::munmap(ptr, length) == -1) { mfem_error("Dealloc error!"); }
350 }
351 
352 /// MMU protection, through ::mprotect with no read/write accesses
353 inline void MmuProtect(const void *ptr, const size_t bytes)
354 {
355  static const bool mmu_protect_error = getenv("MFEM_MMU_PROTECT_ERROR");
356  if (!::mprotect(const_cast<void*>(ptr), bytes, PROT_NONE)) { return; }
357  if (mmu_protect_error) { mfem_error("MMU protection (NONE) error"); }
358 }
359 
360 /// MMU un-protection, through ::mprotect with read/write accesses
361 inline void MmuAllow(const void *ptr, const size_t bytes)
362 {
363  const int RW = PROT_READ | PROT_WRITE;
364  static const bool mmu_protect_error = getenv("MFEM_MMU_PROTECT_ERROR");
365  if (!::mprotect(const_cast<void*>(ptr), bytes, RW)) { return; }
366  if (mmu_protect_error) { mfem_error("MMU protection (R/W) error"); }
367 }
368 #else
369 inline void MmuInit() { }
370 inline void MmuAlloc(void **ptr, const size_t bytes) { *ptr = std::malloc(bytes); }
371 inline void MmuDealloc(void *ptr, const size_t) { std::free(ptr); }
372 inline void MmuProtect(const void*, const size_t) { }
373 inline void MmuAllow(const void*, const size_t) { }
374 inline const void *MmuAddrR(const void *a) { return a; }
375 inline const void *MmuAddrP(const void *a) { return a; }
376 inline uintptr_t MmuLengthR(const void*, const size_t) { return 0; }
377 inline uintptr_t MmuLengthP(const void*, const size_t) { return 0; }
378 #endif
379 
380 /// The MMU host memory space
381 class MmuHostMemorySpace : public HostMemorySpace
382 {
383 public:
384  MmuHostMemorySpace(): HostMemorySpace() { MmuInit(); }
385  void Alloc(void **ptr, size_t bytes) { MmuAlloc(ptr, bytes); }
386  void Dealloc(void *ptr) { MmuDealloc(ptr, maps->memories.at(ptr).bytes); }
387  void Protect(const Memory& mem, size_t bytes)
388  { if (mem.h_rw) { mem.h_rw = false; MmuProtect(mem.h_ptr, bytes); } }
389  void Unprotect(const Memory &mem, size_t bytes)
390  { if (!mem.h_rw) { mem.h_rw = true; MmuAllow(mem.h_ptr, bytes); } }
391  /// Aliases need to be restricted during protection
392  void AliasProtect(const void *ptr, size_t bytes)
393  { MmuProtect(MmuAddrR(ptr), MmuLengthR(ptr, bytes)); }
394  /// Aliases need to be prolongated for un-protection
395  void AliasUnprotect(const void *ptr, size_t bytes)
396  { MmuAllow(MmuAddrP(ptr), MmuLengthP(ptr, bytes)); }
397 };
398 
399 /// The UVM host memory space
400 class UvmHostMemorySpace : public HostMemorySpace
401 {
402 public:
403  UvmHostMemorySpace(): HostMemorySpace() { }
404  void Alloc(void **ptr, size_t bytes) { CuMallocManaged(ptr, bytes == 0 ? 8 : bytes); }
405  void Dealloc(void *ptr) { CuMemFree(ptr); }
406 };
407 
408 /// The 'No' device memory space
409 class NoDeviceMemorySpace: public DeviceMemorySpace
410 {
411 public:
412  void Alloc(internal::Memory&) { mfem_error("! Device Alloc"); }
413  void Dealloc(Memory&) { mfem_error("! Device Dealloc"); }
414  void *HtoD(void*, const void*, size_t) { mfem_error("!HtoD"); return nullptr; }
415  void *DtoD(void*, const void*, size_t) { mfem_error("!DtoD"); return nullptr; }
416  void *DtoH(void*, const void*, size_t) { mfem_error("!DtoH"); return nullptr; }
417 };
418 
419 /// The std:: device memory space, used with the 'debug' device
420 class StdDeviceMemorySpace : public DeviceMemorySpace { };
421 
422 /// The CUDA device memory space
423 class CudaDeviceMemorySpace: public DeviceMemorySpace
424 {
425 public:
426  CudaDeviceMemorySpace(): DeviceMemorySpace() { }
427  void Alloc(Memory &base) { CuMemAlloc(&base.d_ptr, base.bytes); }
428  void Dealloc(Memory &base) { CuMemFree(base.d_ptr); }
429  void *HtoD(void *dst, const void *src, size_t bytes)
430  { return CuMemcpyHtoD(dst, src, bytes); }
431  void *DtoD(void* dst, const void* src, size_t bytes)
432  { return CuMemcpyDtoD(dst, src, bytes); }
433  void *DtoH(void *dst, const void *src, size_t bytes)
434  { return CuMemcpyDtoH(dst, src, bytes); }
435 };
436 
437 /// The CUDA/HIP page-locked host memory space
438 class HostPinnedMemorySpace: public HostMemorySpace
439 {
440 public:
441  HostPinnedMemorySpace(): HostMemorySpace() { }
442  void Alloc(void ** ptr, size_t bytes) override
443  {
444 #ifdef MFEM_USE_CUDA
445  CuMemAllocHostPinned(ptr, bytes);
446 #endif
447 #ifdef MFEM_USE_HIP
448  HipMemAllocHostPinned(ptr, bytes);
449 #endif
450  }
451  void Dealloc(void *ptr) override
452  {
453 #ifdef MFEM_USE_CUDA
454  CuMemFreeHostPinned(ptr);
455 #endif
456 #ifdef MFEM_USE_HIP
458 #endif
459  }
460 };
461 
462 /// The HIP device memory space
463 class HipDeviceMemorySpace: public DeviceMemorySpace
464 {
465 public:
466  HipDeviceMemorySpace(): DeviceMemorySpace() { }
467  void Alloc(Memory &base) { HipMemAlloc(&base.d_ptr, base.bytes); }
468  void Dealloc(Memory &base) { HipMemFree(base.d_ptr); }
469  void *HtoD(void *dst, const void *src, size_t bytes)
470  { return HipMemcpyHtoD(dst, src, bytes); }
471  void *DtoD(void* dst, const void* src, size_t bytes)
472  { return HipMemcpyDtoD(dst, src, bytes); }
473  void *DtoH(void *dst, const void *src, size_t bytes)
474  { return HipMemcpyDtoH(dst, src, bytes); }
475 };
476 
477 /// The UVM device memory space.
478 class UvmCudaMemorySpace : public DeviceMemorySpace
479 {
480 public:
481  void Alloc(Memory &base) { base.d_ptr = base.h_ptr; }
482  void Dealloc(Memory&) { }
483  void *HtoD(void *dst, const void *src, size_t bytes)
484  {
485  if (dst == src) { MFEM_STREAM_SYNC; return dst; }
486  return CuMemcpyHtoD(dst, src, bytes);
487  }
488  void *DtoD(void* dst, const void* src, size_t bytes)
489  { return CuMemcpyDtoD(dst, src, bytes); }
490  void *DtoH(void *dst, const void *src, size_t bytes)
491  {
492  if (dst == src) { MFEM_STREAM_SYNC; return dst; }
493  return CuMemcpyDtoH(dst, src, bytes);
494  }
495 };
496 
497 /// The MMU device memory space
498 class MmuDeviceMemorySpace : public DeviceMemorySpace
499 {
500 public:
501  MmuDeviceMemorySpace(): DeviceMemorySpace() { }
502  void Alloc(Memory &m) { MmuAlloc(&m.d_ptr, m.bytes); }
503  void Dealloc(Memory &m) { MmuDealloc(m.d_ptr, m.bytes); }
504  void Protect(const Memory &m)
505  { if (m.d_rw) { m.d_rw = false; MmuProtect(m.d_ptr, m.bytes); } }
506  void Unprotect(const Memory &m)
507  { if (!m.d_rw) { m.d_rw = true; MmuAllow(m.d_ptr, m.bytes); } }
508  /// Aliases need to be restricted during protection
509  void AliasProtect(const void *ptr, size_t bytes)
510  { MmuProtect(MmuAddrR(ptr), MmuLengthR(ptr, bytes)); }
511  /// Aliases need to be prolongated for un-protection
512  void AliasUnprotect(const void *ptr, size_t bytes)
513  { MmuAllow(MmuAddrP(ptr), MmuLengthP(ptr, bytes)); }
514  void *HtoD(void *dst, const void *src, size_t bytes)
515  { return std::memcpy(dst, src, bytes); }
516  void *DtoD(void *dst, const void *src, size_t bytes)
517  { return std::memcpy(dst, src, bytes); }
518  void *DtoH(void *dst, const void *src, size_t bytes)
519  { return std::memcpy(dst, src, bytes); }
520 };
521 
522 #ifdef MFEM_USE_UMPIRE
523 class UmpireMemorySpace
524 {
525 protected:
526  umpire::ResourceManager &rm;
527  umpire::Allocator allocator;
528  bool owns_allocator{false};
529 
530 public:
531  // TODO: this only releases unused memory
532  virtual ~UmpireMemorySpace() { if (owns_allocator) { allocator.release(); } }
533  UmpireMemorySpace(const char * name, const char * space)
534  : rm(umpire::ResourceManager::getInstance())
535  {
536  if (!rm.isAllocator(name))
537  {
538  allocator = rm.makeAllocator<umpire::strategy::DynamicPool>(
539  name, rm.getAllocator(space));
540  owns_allocator = true;
541  }
542  else
543  {
544  allocator = rm.getAllocator(name);
545  owns_allocator = false;
546  }
547  }
548 };
549 
550 /// The Umpire host memory space
551 class UmpireHostMemorySpace : public HostMemorySpace, public UmpireMemorySpace
552 {
553 private:
554  umpire::strategy::AllocationStrategy *strat;
555 public:
556  UmpireHostMemorySpace(const char * name)
557  : HostMemorySpace(),
558  UmpireMemorySpace(name, "HOST"),
559  strat(allocator.getAllocationStrategy()) {}
560  void Alloc(void **ptr, size_t bytes) override
561  { *ptr = allocator.allocate(bytes); }
562  void Dealloc(void *ptr) override { allocator.deallocate(ptr); }
563  void Insert(void *ptr, size_t bytes)
564  { rm.registerAllocation(ptr, {ptr, bytes, strat}); }
565 };
566 
567 /// The Umpire device memory space
568 #if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
569 class UmpireDeviceMemorySpace : public DeviceMemorySpace,
570  public UmpireMemorySpace
571 {
572 public:
573  UmpireDeviceMemorySpace(const char * name)
574  : DeviceMemorySpace(),
575  UmpireMemorySpace(name, "DEVICE") {}
576  void Alloc(Memory &base) override
577  { base.d_ptr = allocator.allocate(base.bytes); }
578  void Dealloc(Memory &base) override { rm.deallocate(base.d_ptr); }
579  void *HtoD(void *dst, const void *src, size_t bytes) override
580  {
581 #ifdef MFEM_USE_CUDA
582  return CuMemcpyHtoD(dst, src, bytes);
583 #endif
584 #ifdef MFEM_USE_HIP
585  return HipMemcpyHtoD(dst, src, bytes);
586 #endif
587  // rm.copy(dst, const_cast<void*>(src), bytes); return dst;
588  }
589  void *DtoD(void* dst, const void* src, size_t bytes) override
590  {
591 #ifdef MFEM_USE_CUDA
592  return CuMemcpyDtoD(dst, src, bytes);
593 #endif
594 #ifdef MFEM_USE_HIP
595  return HipMemcpyDtoD(dst, src, bytes);
596 #endif
597  // rm.copy(dst, const_cast<void*>(src), bytes); return dst;
598  }
599  void *DtoH(void *dst, const void *src, size_t bytes) override
600  {
601 #ifdef MFEM_USE_CUDA
602  return CuMemcpyDtoH(dst, src, bytes);
603 #endif
604 #ifdef MFEM_USE_HIP
605  return HipMemcpyDtoH(dst, src, bytes);
606 #endif
607  // rm.copy(dst, const_cast<void*>(src), bytes); return dst;
608  }
609 };
610 #else
611 class UmpireDeviceMemorySpace : public NoDeviceMemorySpace
612 {
613 public:
614  UmpireDeviceMemorySpace(const char * /*unused*/) {}
615 };
616 #endif // MFEM_USE_CUDA || MFEM_USE_HIP
617 #endif // MFEM_USE_UMPIRE
618 
619 /// Memory space controller class
620 class Ctrl
621 {
622  typedef MemoryType MT;
623 
624 public:
625  HostMemorySpace *host[HostMemoryTypeSize];
626  DeviceMemorySpace *device[DeviceMemoryTypeSize];
627 
628 public:
629  Ctrl(): host{nullptr}, device{nullptr} { }
630 
631  void Configure()
632  {
633  if (host[HostMemoryType])
634  {
635  mfem_error("Memory backends have already been configured!");
636  }
637 
638  // Filling the host memory backends
639  // HOST, HOST_32 & HOST_64 are always ready
640  // MFEM_USE_UMPIRE will set either [No/Umpire] HostMemorySpace
641  host[static_cast<int>(MT::HOST)] = new StdHostMemorySpace();
642  host[static_cast<int>(MT::HOST_32)] = new Aligned32HostMemorySpace();
643  host[static_cast<int>(MT::HOST_64)] = new Aligned64HostMemorySpace();
644  // HOST_DEBUG is delayed, as it reroutes signals
645  host[static_cast<int>(MT::HOST_DEBUG)] = nullptr;
646  host[static_cast<int>(MT::HOST_UMPIRE)] = nullptr;
647  host[static_cast<int>(MT::MANAGED)] = new UvmHostMemorySpace();
648 
649  // Filling the device memory backends, shifting with the device size
650  constexpr int shift = DeviceMemoryType;
651  device[static_cast<int>(MT::MANAGED)-shift] = new UvmCudaMemorySpace();
652  // All other devices controllers are delayed
653  device[static_cast<int>(MemoryType::DEVICE)-shift] = nullptr;
654  device[static_cast<int>(MT::DEVICE_DEBUG)-shift] = nullptr;
655  device[static_cast<int>(MT::DEVICE_UMPIRE)-shift] = nullptr;
656  device[static_cast<int>(MT::DEVICE_UMPIRE_2)-shift] = nullptr;
657  }
658 
659  HostMemorySpace* Host(const MemoryType mt)
660  {
661  const int mt_i = static_cast<int>(mt);
662  // Delayed host controllers initialization
663  if (!host[mt_i]) { host[mt_i] = NewHostCtrl(mt); }
664  MFEM_ASSERT(host[mt_i], "Host memory controller is not configured!");
665  return host[mt_i];
666  }
667 
668  DeviceMemorySpace* Device(const MemoryType mt)
669  {
670  const int mt_i = static_cast<int>(mt) - DeviceMemoryType;
671  MFEM_ASSERT(mt_i >= 0,"");
672  // Lazy device controller initializations
673  if (!device[mt_i]) { device[mt_i] = NewDeviceCtrl(mt); }
674  MFEM_ASSERT(device[mt_i], "Memory manager has not been configured!");
675  return device[mt_i];
676  }
677 
678  ~Ctrl()
679  {
680  constexpr int mt_h = HostMemoryType;
681  constexpr int mt_d = DeviceMemoryType;
682  for (int mt = mt_h; mt < HostMemoryTypeSize; mt++) { delete host[mt]; }
683  for (int mt = mt_d; mt < MemoryTypeSize; mt++) { delete device[mt-mt_d]; }
684  }
685 
686 private:
687  HostMemorySpace* NewHostCtrl(const MemoryType mt)
688  {
689  switch (mt)
690  {
691  case MT::HOST_DEBUG: return new MmuHostMemorySpace();
692 #ifdef MFEM_USE_UMPIRE
693  case MT::HOST_UMPIRE:
694  return new UmpireHostMemorySpace(
696 #else
697  case MT::HOST_UMPIRE: return new NoHostMemorySpace();
698 #endif
699  case MT::HOST_PINNED: return new HostPinnedMemorySpace();
700  default: MFEM_ABORT("Unknown host memory controller!");
701  }
702  return nullptr;
703  }
704 
705  DeviceMemorySpace* NewDeviceCtrl(const MemoryType mt)
706  {
707  switch (mt)
708  {
709 #ifdef MFEM_USE_UMPIRE
710  case MT::DEVICE_UMPIRE:
711  return new UmpireDeviceMemorySpace(
713  case MT::DEVICE_UMPIRE_2:
714  return new UmpireDeviceMemorySpace(
716 #else
717  case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
718  case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
719 #endif
720  case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
721  case MT::DEVICE:
722  {
723 #if defined(MFEM_USE_CUDA)
724  return new CudaDeviceMemorySpace();
725 #elif defined(MFEM_USE_HIP)
726  return new HipDeviceMemorySpace();
727 #else
728  MFEM_ABORT("No device memory controller!");
729  break;
730 #endif
731  }
732  default: MFEM_ABORT("Unknown device memory controller!");
733  }
734  return nullptr;
735  }
736 };
737 
738 } // namespace mfem::internal
739 
740 static internal::Ctrl *ctrl;
741 
742 void *MemoryManager::New_(void *h_tmp, size_t bytes, MemoryType mt,
743  unsigned &flags)
744 {
745  MFEM_ASSERT(exists, "Internal error!");
746  if (IsHostMemory(mt))
747  {
748  MFEM_ASSERT(mt != MemoryType::HOST && h_tmp == nullptr,
749  "Internal error!");
750  // d_mt = MemoryType::DEFAULT means d_mt = GetDualMemoryType(h_mt),
751  // evaluated at the time when the device pointer is allocated, see
752  // GetDevicePtr() and GetAliasDevicePtr()
753  const MemoryType d_mt = MemoryType::DEFAULT;
754  // We rely on the next call using lazy dev alloc
755  return New_(h_tmp, bytes, mt, d_mt, Mem::VALID_HOST, flags);
756  }
757  else
758  {
759  const MemoryType h_mt = GetDualMemoryType(mt);
760  return New_(h_tmp, bytes, h_mt, mt, Mem::VALID_DEVICE, flags);
761  }
762 }
763 
764 void *MemoryManager::New_(void *h_tmp, size_t bytes, MemoryType h_mt,
765  MemoryType d_mt, unsigned valid_flags,
766  unsigned &flags)
767 {
768  MFEM_ASSERT(exists, "Internal error!");
769  MFEM_ASSERT(IsHostMemory(h_mt), "h_mt must be host type");
770  MFEM_ASSERT(IsDeviceMemory(d_mt) || d_mt == h_mt ||
771  d_mt == MemoryType::DEFAULT,
772  "d_mt must be device type, the same is h_mt, or DEFAULT");
773  MFEM_ASSERT((h_mt != MemoryType::HOST || h_tmp != nullptr) &&
774  (h_mt == MemoryType::HOST || h_tmp == nullptr),
775  "Internal error");
776  MFEM_ASSERT((valid_flags & ~(Mem::VALID_HOST | Mem::VALID_DEVICE)) == 0,
777  "Internal error");
778  void *h_ptr;
779  if (h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
780  else { h_ptr = h_tmp; }
782  Mem::OWNS_DEVICE | valid_flags;
783  // The other New_() method relies on this lazy allocation behavior.
784  mm.Insert(h_ptr, bytes, h_mt, d_mt); // lazy dev alloc
785  // mm.InsertDevice(nullptr, h_ptr, bytes, h_mt, d_mt); // non-lazy dev alloc
786 
787  // MFEM_VERIFY_TYPES(h_mt, mt); // done by mm.Insert() above
788  CheckHostMemoryType_(h_mt, h_ptr, false);
789 
790  return h_ptr;
791 }
792 
793 void *MemoryManager::Register_(void *ptr, void *h_tmp, size_t bytes,
794  MemoryType mt,
795  bool own, bool alias, unsigned &flags)
796 {
797  MFEM_CONTRACT_VAR(alias);
798  MFEM_ASSERT(exists, "Internal error!");
799  MFEM_VERIFY(!alias, "Cannot register an alias!");
800  const bool is_host_mem = IsHostMemory(mt);
801  const MemType h_mt = is_host_mem ? mt : GetDualMemoryType(mt);
802  const MemType d_mt = is_host_mem ? MemoryType::DEFAULT : mt;
803  // d_mt = MemoryType::DEFAULT means d_mt = GetDualMemoryType(h_mt),
804  // evaluated at the time when the device pointer is allocated, see
805  // GetDevicePtr() and GetAliasDevicePtr()
806 
807  MFEM_VERIFY_TYPES(h_mt, d_mt);
808 
809  if (ptr == nullptr && h_tmp == nullptr)
810  {
811  MFEM_VERIFY(bytes == 0, "internal error");
812  return nullptr;
813  }
814 
816  void *h_ptr;
817 
818  if (is_host_mem) // HOST TYPES + MANAGED
819  {
820  h_ptr = ptr;
821  mm.Insert(h_ptr, bytes, h_mt, d_mt);
822  flags = (own ? flags | Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST) |
824  }
825  else // DEVICE TYPES
826  {
827  MFEM_VERIFY(ptr || bytes == 0,
828  "cannot register NULL device pointer with bytes = " << bytes);
829  if (h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
830  else { h_ptr = h_tmp; }
831  mm.InsertDevice(ptr, h_ptr, bytes, h_mt, d_mt);
832  flags = own ? flags | Mem::OWNS_DEVICE : flags & ~Mem::OWNS_DEVICE;
833  flags |= (Mem::OWNS_HOST | Mem::VALID_DEVICE);
834  }
835  CheckHostMemoryType_(h_mt, h_ptr, alias);
836  return h_ptr;
837 }
838 
839 void MemoryManager::Register2_(void *h_ptr, void *d_ptr, size_t bytes,
840  MemoryType h_mt, MemoryType d_mt,
841  bool own, bool alias, unsigned &flags)
842 {
843  MFEM_CONTRACT_VAR(alias);
844  MFEM_ASSERT(exists, "Internal error!");
845  MFEM_ASSERT(!alias, "Cannot register an alias!");
846  MFEM_VERIFY_TYPES(h_mt, d_mt);
847 
848  if (h_ptr == nullptr && d_ptr == nullptr)
849  {
850  MFEM_VERIFY(bytes == 0, "internal error");
851  return;
852  }
853 
855 
856  MFEM_VERIFY(d_ptr || bytes == 0,
857  "cannot register NULL device pointer with bytes = " << bytes);
858  mm.InsertDevice(d_ptr, h_ptr, bytes, h_mt, d_mt);
859  flags = (own ? flags | (Mem::OWNS_HOST | Mem::OWNS_DEVICE) :
860  flags & ~(Mem::OWNS_HOST | Mem::OWNS_DEVICE)) |
862 
863  CheckHostMemoryType_(h_mt, h_ptr, alias);
864 }
865 
866 void MemoryManager::Alias_(void *base_h_ptr, size_t offset, size_t bytes,
867  unsigned base_flags, unsigned &flags)
868 {
869  mm.InsertAlias(base_h_ptr, (char*)base_h_ptr + offset, bytes,
870  base_flags & Mem::ALIAS);
871  flags = (base_flags | Mem::ALIAS | Mem::OWNS_INTERNAL) &
873 }
874 
875 void MemoryManager::SetDeviceMemoryType_(void *h_ptr, unsigned flags,
876  MemoryType d_mt)
877 {
878  MFEM_VERIFY(h_ptr, "cannot set the device memory type: Memory is empty!");
879  if (!(flags & Mem::ALIAS))
880  {
881  auto mem_iter = maps->memories.find(h_ptr);
882  MFEM_VERIFY(mem_iter != maps->memories.end(), "internal error");
883  internal::Memory &mem = mem_iter->second;
884  if (mem.d_mt == d_mt) { return; }
885  MFEM_VERIFY(mem.d_ptr == nullptr, "cannot set the device memory type:"
886  " device memory is allocated!");
887  mem.d_mt = d_mt;
888  }
889  else
890  {
891  auto alias_iter = maps->aliases.find(h_ptr);
892  MFEM_VERIFY(alias_iter != maps->aliases.end(), "internal error");
893  internal::Alias &alias = alias_iter->second;
894  internal::Memory &base_mem = *alias.mem;
895  if (base_mem.d_mt == d_mt) { return; }
896  MFEM_VERIFY(base_mem.d_ptr == nullptr,
897  "cannot set the device memory type:"
898  " alias' base device memory is allocated!");
899  base_mem.d_mt = d_mt;
900  }
901 }
902 
903 MemoryType MemoryManager::Delete_(void *h_ptr, MemoryType h_mt, unsigned flags)
904 {
905  const bool alias = flags & Mem::ALIAS;
906  const bool registered = flags & Mem::REGISTERED;
907  const bool owns_host = flags & Mem::OWNS_HOST;
908  const bool owns_device = flags & Mem::OWNS_DEVICE;
909  const bool owns_internal = flags & Mem::OWNS_INTERNAL;
910  MFEM_ASSERT(IsHostMemory(h_mt), "invalid h_mt = " << (int)h_mt);
911  // MFEM_ASSERT(registered || IsHostMemory(h_mt),"");
912  MFEM_ASSERT(!owns_device || owns_internal, "invalid Memory state");
913  MFEM_ASSERT(registered || !(owns_host || owns_device || owns_internal),
914  "invalid Memory state");
915  if (!mm.exists || !registered) { return h_mt; }
916  if (alias)
917  {
918  if (owns_internal)
919  {
920  MFEM_ASSERT(mm.IsAlias(h_ptr), "");
921  MFEM_ASSERT(h_mt == maps->aliases.at(h_ptr).h_mt, "");
922  mm.EraseAlias(h_ptr);
923  }
924  }
925  else // Known
926  {
927  if (owns_host && (h_mt != MemoryType::HOST))
928  { ctrl->Host(h_mt)->Dealloc(h_ptr); }
929  if (owns_internal)
930  {
931  MFEM_ASSERT(mm.IsKnown(h_ptr), "");
932  MFEM_ASSERT(h_mt == maps->memories.at(h_ptr).h_mt, "");
933  mm.Erase(h_ptr, owns_device);
934  }
935  }
936  return h_mt;
937 }
938 
939 void MemoryManager::DeleteDevice_(void *h_ptr, unsigned & flags)
940 {
941  const bool owns_device = flags & Mem::OWNS_DEVICE;
942  if (owns_device)
943  {
944  mm.EraseDevice(h_ptr);
945  flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
946  }
947 }
948 
949 bool MemoryManager::MemoryClassCheck_(MemoryClass mc, void *h_ptr,
950  MemoryType h_mt, size_t bytes,
951  unsigned flags)
952 {
953  if (!h_ptr)
954  {
955  MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
956  return true;
957  }
958  MemoryType d_mt;
959  if (!(flags & Mem::ALIAS))
960  {
961  auto iter = maps->memories.find(h_ptr);
962  MFEM_VERIFY(iter != maps->memories.end(), "internal error");
963  d_mt = iter->second.d_mt;
964  }
965  else
966  {
967  auto iter = maps->aliases.find(h_ptr);
968  MFEM_VERIFY(iter != maps->aliases.end(), "internal error");
969  d_mt = iter->second.mem->d_mt;
970  }
971  if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
972  switch (mc)
973  {
975  {
976  MFEM_VERIFY(h_mt == MemoryType::HOST_32 ||
977  h_mt == MemoryType::HOST_64,"");
978  return true;
979  }
981  {
982  MFEM_VERIFY(h_mt == MemoryType::HOST_64,"");
983  return true;
984  }
985  case MemoryClass::DEVICE:
986  {
987  MFEM_VERIFY(d_mt == MemoryType::DEVICE ||
988  d_mt == MemoryType::DEVICE_DEBUG ||
989  d_mt == MemoryType::DEVICE_UMPIRE ||
990  d_mt == MemoryType::DEVICE_UMPIRE_2 ||
991  d_mt == MemoryType::MANAGED,"");
992  return true;
993  }
995  {
996  MFEM_VERIFY((h_mt == MemoryType::MANAGED &&
997  d_mt == MemoryType::MANAGED),"");
998  return true;
999  }
1000  default: break;
1001  }
1002  return true;
1003 }
1004 
1005 void *MemoryManager::ReadWrite_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1006  size_t bytes, unsigned &flags)
1007 {
1008  if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1009  if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,""); }
1010  MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1012  {
1013  const bool copy = !(flags & Mem::VALID_HOST);
1014  flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
1015  if (flags & Mem::ALIAS)
1016  { return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
1017  else { return mm.GetHostPtr(h_ptr, bytes, copy); }
1018  }
1019  else
1020  {
1021  const bool copy = !(flags & Mem::VALID_DEVICE);
1022  flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
1023  if (flags & Mem::ALIAS)
1024  { return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
1025  else { return mm.GetDevicePtr(h_ptr, bytes, copy); }
1026  }
1027 }
1028 
1029 const void *MemoryManager::Read_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1030  size_t bytes, unsigned &flags)
1031 {
1032  if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1033  if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,""); }
1034  MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1036  {
1037  const bool copy = !(flags & Mem::VALID_HOST);
1038  flags |= Mem::VALID_HOST;
1039  if (flags & Mem::ALIAS)
1040  { return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
1041  else { return mm.GetHostPtr(h_ptr, bytes, copy); }
1042  }
1043  else
1044  {
1045  const bool copy = !(flags & Mem::VALID_DEVICE);
1046  flags |= Mem::VALID_DEVICE;
1047  if (flags & Mem::ALIAS)
1048  { return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
1049  else { return mm.GetDevicePtr(h_ptr, bytes, copy); }
1050  }
1051 }
1052 
1053 void *MemoryManager::Write_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1054  size_t bytes, unsigned &flags)
1055 {
1056  if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1057  if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,""); }
1058  MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1060  {
1061  flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
1062  if (flags & Mem::ALIAS)
1063  { return mm.GetAliasHostPtr(h_ptr, bytes, false); }
1064  else { return mm.GetHostPtr(h_ptr, bytes, false); }
1065  }
1066  else
1067  {
1068  flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
1069  if (flags & Mem::ALIAS)
1070  { return mm.GetAliasDevicePtr(h_ptr, bytes, false); }
1071  else { return mm.GetDevicePtr(h_ptr, bytes, false); }
1072  }
1073 }
1074 
1075 void MemoryManager::SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
1076  size_t alias_bytes, unsigned base_flags,
1077  unsigned &alias_flags)
1078 {
1079  // This is called only when (base_flags & Mem::REGISTERED) is true.
1080  // Note that (alias_flags & REGISTERED) may not be true.
1081  MFEM_ASSERT(alias_flags & Mem::ALIAS, "not an alias");
1082  if ((base_flags & Mem::VALID_HOST) && !(alias_flags & Mem::VALID_HOST))
1083  {
1084  mm.GetAliasHostPtr(alias_h_ptr, alias_bytes, true);
1085  }
1086  if ((base_flags & Mem::VALID_DEVICE) && !(alias_flags & Mem::VALID_DEVICE))
1087  {
1088  if (!(alias_flags & Mem::REGISTERED))
1089  {
1090  mm.InsertAlias(base_h_ptr, alias_h_ptr, alias_bytes, base_flags & Mem::ALIAS);
1091  alias_flags = (alias_flags | Mem::REGISTERED | Mem::OWNS_INTERNAL) &
1092  ~(Mem::OWNS_HOST | Mem::OWNS_DEVICE);
1093  }
1094  mm.GetAliasDevicePtr(alias_h_ptr, alias_bytes, true);
1095  }
1096  alias_flags = (alias_flags & ~(Mem::VALID_HOST | Mem::VALID_DEVICE)) |
1097  (base_flags & (Mem::VALID_HOST | Mem::VALID_DEVICE));
1098 }
1099 
1100 MemoryType MemoryManager::GetDeviceMemoryType_(void *h_ptr, bool alias)
1101 {
1102  if (mm.exists)
1103  {
1104  if (!alias)
1105  {
1106  auto iter = maps->memories.find(h_ptr);
1107  MFEM_ASSERT(iter != maps->memories.end(), "internal error");
1108  return iter->second.d_mt;
1109  }
1110  // alias == true
1111  auto iter = maps->aliases.find(h_ptr);
1112  MFEM_ASSERT(iter != maps->aliases.end(), "internal error");
1113  return iter->second.mem->d_mt;
1114  }
1115  MFEM_ABORT("internal error");
1116  return MemoryManager::host_mem_type;
1117 }
1118 
1119 MemoryType MemoryManager::GetHostMemoryType_(void *h_ptr)
1120 {
1121  if (!mm.exists) { return MemoryManager::host_mem_type; }
1122  if (mm.IsKnown(h_ptr)) { return maps->memories.at(h_ptr).h_mt; }
1123  if (mm.IsAlias(h_ptr)) { return maps->aliases.at(h_ptr).h_mt; }
1124  return MemoryManager::host_mem_type;
1125 }
1126 
1127 void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
1128  size_t bytes, unsigned src_flags,
1129  unsigned &dst_flags)
1130 {
1131  // Type of copy to use based on the src and dest validity flags:
1132  // | src
1133  // | h | d | hd
1134  // -----------+-----+-----+------
1135  // h | h2h d2h h2h
1136  // dest d | h2d d2d d2d
1137  // hd | h2h d2d d2d
1138 
1139  const bool dst_on_host =
1140  (dst_flags & Mem::VALID_HOST) &&
1141  (!(dst_flags & Mem::VALID_DEVICE) ||
1142  ((src_flags & Mem::VALID_HOST) && !(src_flags & Mem::VALID_DEVICE)));
1143 
1144  dst_flags = dst_flags &
1145  ~(dst_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
1146 
1147  const bool src_on_host =
1148  (src_flags & Mem::VALID_HOST) &&
1149  (!(src_flags & Mem::VALID_DEVICE) ||
1150  ((dst_flags & Mem::VALID_HOST) && !(dst_flags & Mem::VALID_DEVICE)));
1151 
1152  const void *src_d_ptr =
1153  src_on_host ? NULL :
1154  ((src_flags & Mem::ALIAS) ?
1155  mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
1156  mm.GetDevicePtr(src_h_ptr, bytes, false));
1157 
1158  if (dst_on_host)
1159  {
1160  if (src_on_host)
1161  {
1162  if (dst_h_ptr != src_h_ptr && bytes != 0)
1163  {
1164  MFEM_ASSERT((const char*)dst_h_ptr + bytes <= src_h_ptr ||
1165  (const char*)src_h_ptr + bytes <= dst_h_ptr,
1166  "data overlaps!");
1167  std::memcpy(dst_h_ptr, src_h_ptr, bytes);
1168  }
1169  }
1170  else
1171  {
1172  if (dst_h_ptr != src_d_ptr && bytes != 0)
1173  {
1174  internal::Memory &src_d_base = maps->memories.at(src_h_ptr);
1175  MemoryType src_d_mt = src_d_base.d_mt;
1176  ctrl->Device(src_d_mt)->DtoH(dst_h_ptr, src_d_ptr, bytes);
1177  }
1178  }
1179  }
1180  else
1181  {
1182  void *dest_d_ptr = (dst_flags & Mem::ALIAS) ?
1183  mm.GetAliasDevicePtr(dst_h_ptr, bytes, false) :
1184  mm.GetDevicePtr(dst_h_ptr, bytes, false);
1185  if (src_on_host)
1186  {
1187  const bool known = mm.IsKnown(dst_h_ptr);
1188  const bool alias = dst_flags & Mem::ALIAS;
1189  MFEM_VERIFY(alias||known,"");
1190  const MemoryType d_mt = known ?
1191  maps->memories.at(dst_h_ptr).d_mt :
1192  maps->aliases.at(dst_h_ptr).mem->d_mt;
1193  ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
1194  }
1195  else
1196  {
1197  if (dest_d_ptr != src_d_ptr && bytes != 0)
1198  {
1199  const bool known = mm.IsKnown(dst_h_ptr);
1200  const bool alias = dst_flags & Mem::ALIAS;
1201  MFEM_VERIFY(alias||known,"");
1202  const MemoryType d_mt = known ?
1203  maps->memories.at(dst_h_ptr).d_mt :
1204  maps->aliases.at(dst_h_ptr).mem->d_mt;
1205  ctrl->Device(d_mt)->DtoD(dest_d_ptr, src_d_ptr, bytes);
1206  }
1207  }
1208  }
1209 }
1210 
1211 void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
1212  size_t bytes, unsigned src_flags)
1213 {
1214  const bool src_on_host = src_flags & Mem::VALID_HOST;
1215  if (src_on_host)
1216  {
1217  if (dest_h_ptr != src_h_ptr && bytes != 0)
1218  {
1219  MFEM_ASSERT((char*)dest_h_ptr + bytes <= src_h_ptr ||
1220  (const char*)src_h_ptr + bytes <= dest_h_ptr,
1221  "data overlaps!");
1222  std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1223  }
1224  }
1225  else
1226  {
1227  MFEM_ASSERT(IsKnown_(src_h_ptr), "internal error");
1228  const void *src_d_ptr = (src_flags & Mem::ALIAS) ?
1229  mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
1230  mm.GetDevicePtr(src_h_ptr, bytes, false);
1231  const internal::Memory &base = maps->memories.at(dest_h_ptr);
1232  const MemoryType d_mt = base.d_mt;
1233  ctrl->Device(d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
1234  }
1235 }
1236 
1237 void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
1238  size_t bytes, unsigned &dest_flags)
1239 {
1240  const bool dest_on_host = dest_flags & Mem::VALID_HOST;
1241  if (dest_on_host)
1242  {
1243  if (dest_h_ptr != src_h_ptr && bytes != 0)
1244  {
1245  MFEM_ASSERT((char*)dest_h_ptr + bytes <= src_h_ptr ||
1246  (const char*)src_h_ptr + bytes <= dest_h_ptr,
1247  "data overlaps!");
1248  std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1249  }
1250  }
1251  else
1252  {
1253  void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
1254  mm.GetAliasDevicePtr(dest_h_ptr, bytes, false) :
1255  mm.GetDevicePtr(dest_h_ptr, bytes, false);
1256  const internal::Memory &base = maps->memories.at(dest_h_ptr);
1257  const MemoryType d_mt = base.d_mt;
1258  ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
1259  }
1260  dest_flags = dest_flags &
1261  ~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
1262 }
1263 
1264 bool MemoryManager::IsKnown_(const void *h_ptr)
1265 {
1266  return maps->memories.find(h_ptr) != maps->memories.end();
1267 }
1268 
1269 bool MemoryManager::IsAlias_(const void *h_ptr)
1270 {
1271  return maps->aliases.find(h_ptr) != maps->aliases.end();
1272 }
1273 
1274 void MemoryManager::Insert(void *h_ptr, size_t bytes,
1275  MemoryType h_mt, MemoryType d_mt)
1276 {
1277 #ifdef MFEM_TRACK_MEM_MANAGER
1278  mfem::out << "[mfem memory manager]: registering h_ptr: " << h_ptr
1279  << ", bytes: " << bytes << std::endl;
1280 #endif
1281  if (h_ptr == NULL)
1282  {
1283  MFEM_VERIFY(bytes == 0, "Trying to add NULL with size " << bytes);
1284  return;
1285  }
1286  MFEM_VERIFY_TYPES(h_mt, d_mt);
1287 #ifdef MFEM_DEBUG
1288  auto res =
1289 #endif
1290  maps->memories.emplace(h_ptr, internal::Memory(h_ptr, bytes, h_mt, d_mt));
1291 #ifdef MFEM_DEBUG
1292  if (res.second == false)
1293  {
1294  auto &m = res.first->second;
1295  MFEM_VERIFY(m.bytes >= bytes && m.h_mt == h_mt &&
1296  (m.d_mt == d_mt || (d_mt == MemoryType::DEFAULT &&
1297  m.d_mt == GetDualMemoryType(h_mt))),
1298  "Address already present with different attributes!");
1299 #ifdef MFEM_TRACK_MEM_MANAGER
1300  mfem::out << "[mfem memory manager]: repeated registration of h_ptr: "
1301  << h_ptr << std::endl;
1302 #endif
1303  }
1304 #endif
1305 }
1306 
1307 void MemoryManager::InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
1308  MemoryType h_mt, MemoryType d_mt)
1309 {
1310  // MFEM_VERIFY_TYPES(h_mt, d_mt); // done by Insert() below
1311  MFEM_ASSERT(h_ptr != NULL, "internal error");
1312  Insert(h_ptr, bytes, h_mt, d_mt);
1313  internal::Memory &mem = maps->memories.at(h_ptr);
1314  if (d_ptr == NULL && bytes != 0) { ctrl->Device(d_mt)->Alloc(mem); }
1315  else { mem.d_ptr = d_ptr; }
1316 }
1317 
1318 void MemoryManager::InsertAlias(const void *base_ptr, void *alias_ptr,
1319  const size_t bytes, const bool base_is_alias)
1320 {
1321  size_t offset = static_cast<size_t>(static_cast<const char*>(alias_ptr) -
1322  static_cast<const char*>(base_ptr));
1323 #ifdef MFEM_TRACK_MEM_MANAGER
1324  mfem::out << "[mfem memory manager]: registering alias of base_ptr: "
1325  << base_ptr << ", offset: " << offset << ", bytes: " << bytes
1326  << ", base is alias: " << base_is_alias << std::endl;
1327 #endif
1328  if (!base_ptr)
1329  {
1330  MFEM_VERIFY(offset == 0,
1331  "Trying to add alias to NULL at offset " << offset);
1332  return;
1333  }
1334  if (base_is_alias)
1335  {
1336  const internal::Alias &alias = maps->aliases.at(base_ptr);
1337  MFEM_ASSERT(alias.mem,"");
1338  base_ptr = alias.mem->h_ptr;
1339  offset += alias.offset;
1340 #ifdef MFEM_TRACK_MEM_MANAGER
1341  mfem::out << "[mfem memory manager]: real base_ptr: " << base_ptr
1342  << std::endl;
1343 #endif
1344  }
1345  internal::Memory &mem = maps->memories.at(base_ptr);
1346  MFEM_VERIFY(offset + bytes <= mem.bytes, "invalid alias");
1347  auto res =
1348  maps->aliases.emplace(alias_ptr,
1349  internal::Alias{&mem, offset, 1, mem.h_mt});
1350  if (res.second == false) // alias_ptr was already in the map
1351  {
1352  internal::Alias &alias = res.first->second;
1353  // Update the alias data in case the existing alias is dangling
1354  alias.mem = &mem;
1355  alias.offset = offset;
1356  alias.h_mt = mem.h_mt;
1357  alias.counter++;
1358  }
1359 }
1360 
1361 void MemoryManager::Erase(void *h_ptr, bool free_dev_ptr)
1362 {
1363 #ifdef MFEM_TRACK_MEM_MANAGER
1364  mfem::out << "[mfem memory manager]: un-registering h_ptr: " << h_ptr
1365  << std::endl;
1366 #endif
1367  if (!h_ptr) { return; }
1368  auto mem_map_iter = maps->memories.find(h_ptr);
1369  if (mem_map_iter == maps->memories.end()) { mfem_error("Unknown pointer!"); }
1370  internal::Memory &mem = mem_map_iter->second;
1371  if (mem.d_ptr && free_dev_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem);}
1372  maps->memories.erase(mem_map_iter);
1373 }
1374 
1375 void MemoryManager::EraseDevice(void *h_ptr)
1376 {
1377  if (!h_ptr) { return; }
1378  auto mem_map_iter = maps->memories.find(h_ptr);
1379  if (mem_map_iter == maps->memories.end()) { mfem_error("Unknown pointer!"); }
1380  internal::Memory &mem = mem_map_iter->second;
1381  if (mem.d_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem);}
1382  mem.d_ptr = nullptr;
1383 }
1384 
1385 void MemoryManager::EraseAlias(void *alias_ptr)
1386 {
1387 #ifdef MFEM_TRACK_MEM_MANAGER
1388  mfem::out << "[mfem memory manager]: un-registering alias_ptr: " << alias_ptr
1389  << std::endl;
1390 #endif
1391  if (!alias_ptr) { return; }
1392  auto alias_map_iter = maps->aliases.find(alias_ptr);
1393  if (alias_map_iter == maps->aliases.end()) { mfem_error("Unknown alias!"); }
1394  internal::Alias &alias = alias_map_iter->second;
1395  if (--alias.counter) { return; }
1396  maps->aliases.erase(alias_map_iter);
1397 }
1398 
1399 void *MemoryManager::GetDevicePtr(const void *h_ptr, size_t bytes,
1400  bool copy_data)
1401 {
1402  if (!h_ptr)
1403  {
1404  MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
1405  return NULL;
1406  }
1407  internal::Memory &mem = maps->memories.at(h_ptr);
1408  const MemoryType &h_mt = mem.h_mt;
1409  MemoryType &d_mt = mem.d_mt;
1410  MFEM_VERIFY_TYPES(h_mt, d_mt);
1411  if (!mem.d_ptr)
1412  {
1413  if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
1414  if (mem.bytes) { ctrl->Device(d_mt)->Alloc(mem); }
1415  }
1416  // Aliases might have done some protections
1417  if (mem.d_ptr) { ctrl->Device(d_mt)->Unprotect(mem); }
1418  if (copy_data)
1419  {
1420  MFEM_ASSERT(bytes <= mem.bytes, "invalid copy size");
1421  if (bytes) { ctrl->Device(d_mt)->HtoD(mem.d_ptr, h_ptr, bytes); }
1422  }
1423  ctrl->Host(h_mt)->Protect(mem, bytes);
1424  return mem.d_ptr;
1425 }
1426 
1427 void *MemoryManager::GetAliasDevicePtr(const void *alias_ptr, size_t bytes,
1428  bool copy)
1429 {
1430  if (!alias_ptr)
1431  {
1432  MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
1433  return NULL;
1434  }
1435  auto &alias_map = maps->aliases;
1436  auto alias_map_iter = alias_map.find(alias_ptr);
1437  if (alias_map_iter == alias_map.end()) { mfem_error("alias not found"); }
1438  const internal::Alias &alias = alias_map_iter->second;
1439  const size_t offset = alias.offset;
1440  internal::Memory &mem = *alias.mem;
1441  const MemoryType &h_mt = mem.h_mt;
1442  MemoryType &d_mt = mem.d_mt;
1443  MFEM_VERIFY_TYPES(h_mt, d_mt);
1444  if (!mem.d_ptr)
1445  {
1446  if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
1447  if (mem.bytes) { ctrl->Device(d_mt)->Alloc(mem); }
1448  }
1449  void *alias_h_ptr = static_cast<char*>(mem.h_ptr) + offset;
1450  void *alias_d_ptr = static_cast<char*>(mem.d_ptr) + offset;
1451  MFEM_ASSERT(alias_h_ptr == alias_ptr, "internal error");
1452  MFEM_ASSERT(offset + bytes <= mem.bytes, "internal error");
1453  mem.d_rw = mem.h_rw = false;
1454  if (mem.d_ptr) { ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes); }
1455  ctrl->Host(h_mt)->AliasUnprotect(alias_ptr, bytes);
1456  if (copy && mem.d_ptr)
1457  { ctrl->Device(d_mt)->HtoD(alias_d_ptr, alias_h_ptr, bytes); }
1458  ctrl->Host(h_mt)->AliasProtect(alias_ptr, bytes);
1459  return alias_d_ptr;
1460 }
1461 
1462 void *MemoryManager::GetHostPtr(const void *ptr, size_t bytes, bool copy)
1463 {
1464  const internal::Memory &mem = maps->memories.at(ptr);
1465  MFEM_ASSERT(mem.h_ptr == ptr, "internal error");
1466  MFEM_ASSERT(bytes <= mem.bytes, "internal error")
1467  const MemoryType &h_mt = mem.h_mt;
1468  const MemoryType &d_mt = mem.d_mt;
1469  MFEM_VERIFY_TYPES(h_mt, d_mt);
1470  // Aliases might have done some protections
1471  ctrl->Host(h_mt)->Unprotect(mem, bytes);
1472  if (mem.d_ptr) { ctrl->Device(d_mt)->Unprotect(mem); }
1473  if (copy && mem.d_ptr) { ctrl->Device(d_mt)->DtoH(mem.h_ptr, mem.d_ptr, bytes); }
1474  if (mem.d_ptr) { ctrl->Device(d_mt)->Protect(mem); }
1475  return mem.h_ptr;
1476 }
1477 
1478 void *MemoryManager::GetAliasHostPtr(const void *ptr, size_t bytes,
1479  bool copy_data)
1480 {
1481  const internal::Alias &alias = maps->aliases.at(ptr);
1482  const internal::Memory *const mem = alias.mem;
1483  const MemoryType &h_mt = mem->h_mt;
1484  const MemoryType &d_mt = mem->d_mt;
1485  MFEM_VERIFY_TYPES(h_mt, d_mt);
1486  void *alias_h_ptr = static_cast<char*>(mem->h_ptr) + alias.offset;
1487  void *alias_d_ptr = static_cast<char*>(mem->d_ptr) + alias.offset;
1488  MFEM_ASSERT(alias_h_ptr == ptr, "internal error");
1489  mem->h_rw = false;
1490  ctrl->Host(h_mt)->AliasUnprotect(alias_h_ptr, bytes);
1491  if (mem->d_ptr) { ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes); }
1492  if (copy_data && mem->d_ptr)
1493  { ctrl->Device(d_mt)->DtoH(const_cast<void*>(ptr), alias_d_ptr, bytes); }
1494  if (mem->d_ptr) { ctrl->Device(d_mt)->AliasProtect(alias_d_ptr, bytes); }
1495  return alias_h_ptr;
1496 }
1497 
1499 {
1500  if (exists) { return; }
1501  maps = new internal::Maps();
1502  ctrl = new internal::Ctrl();
1503  ctrl->Configure();
1504  exists = true;
1505 }
1506 
1508 
1509 MemoryManager::~MemoryManager() { if (exists) { Destroy(); } }
1510 
1512 {
1513  MFEM_VERIFY(!configured, "changing the dual MemoryTypes is not allowed after"
1514  " MemoryManager configuration!");
1515  UpdateDualMemoryType(mt, dual_mt);
1516 }
1517 
1518 void MemoryManager::UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt)
1519 {
1520  MFEM_VERIFY((int)mt < MemoryTypeSize,
1521  "invalid MemoryType, mt = " << (int)mt);
1522  MFEM_VERIFY((int)dual_mt < MemoryTypeSize,
1523  "invalid dual MemoryType, dual_mt = " << (int)dual_mt);
1524 
1525  if ((IsHostMemory(mt) && IsDeviceMemory(dual_mt)) ||
1526  (IsDeviceMemory(mt) && IsHostMemory(dual_mt)))
1527  {
1528  dual_map[(int)mt] = dual_mt;
1529  }
1530  else
1531  {
1532  // mt + dual_mt is not a pair of host + device types: this is only allowed
1533  // when mt == dual_mt and mt is a host type; in this case we do not
1534  // actually update the dual
1535  MFEM_VERIFY(mt == dual_mt && IsHostMemory(mt),
1536  "invalid (mt, dual_mt) pair: ("
1537  << MemoryTypeName[(int)mt] << ", "
1538  << MemoryTypeName[(int)dual_mt] << ')');
1539  }
1540 }
1541 
1543  const MemoryType device_mt)
1544 {
1545  MemoryManager::UpdateDualMemoryType(host_mt, device_mt);
1546  MemoryManager::UpdateDualMemoryType(device_mt, host_mt);
1547  if (device_mt == MemoryType::DEVICE_DEBUG)
1548  {
1549  for (int mt = (int)MemoryType::HOST; mt < (int)MemoryType::MANAGED; mt++)
1550  {
1551  MemoryManager::UpdateDualMemoryType(
1553  }
1554  }
1555  Init();
1556  host_mem_type = host_mt;
1557  device_mem_type = device_mt;
1558  configured = true;
1559 }
1560 
1562 {
1563  MFEM_VERIFY(exists, "MemoryManager has already been destroyed!");
1564 #ifdef MFEM_TRACK_MEM_MANAGER
1565  size_t num_memories = maps->memories.size();
1566  size_t num_aliases = maps->aliases.size();
1567  if (num_memories != 0 || num_aliases != 0)
1568  {
1569  MFEM_WARNING("...\n\t number of registered pointers: " << num_memories
1570  << "\n\t number of registered aliases : " << num_aliases);
1571  }
1572 #endif
1573  // Keep for debugging purposes:
1574 #if 0
1575  mfem::out << "Destroying the MemoryManager ...\n"
1576  << "remaining registered pointers : "
1577  << maps->memories.size() << '\n'
1578  << "remaining registered aliases : "
1579  << maps->aliases.size() << '\n';
1580 #endif
1581  for (auto& n : maps->memories)
1582  {
1583  internal::Memory &mem = n.second;
1584  bool mem_h_ptr = mem.h_mt != MemoryType::HOST && mem.h_ptr;
1585  if (mem_h_ptr) { ctrl->Host(mem.h_mt)->Dealloc(mem.h_ptr); }
1586  if (mem.d_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem); }
1587  }
1588  delete maps; maps = nullptr;
1589  delete ctrl; ctrl = nullptr;
1590  host_mem_type = MemoryType::HOST;
1591  device_mem_type = MemoryType::HOST;
1592  exists = false;
1593  configured = false;
1594 }
1595 
1597 {
1598  if (ptr != NULL)
1599  {
1600  if (!IsKnown(ptr))
1601  {
1602  mfem_error("Pointer is not registered!");
1603  }
1604  }
1605 }
1606 
1607 int MemoryManager::PrintPtrs(std::ostream &out)
1608 {
1609  int n_out = 0;
1610  for (const auto& n : maps->memories)
1611  {
1612  const internal::Memory &mem = n.second;
1613  out << "\nkey " << n.first << ", "
1614  << "h_ptr " << mem.h_ptr << ", "
1615  << "d_ptr " << mem.d_ptr;
1616  n_out++;
1617  }
1618  if (maps->memories.size() > 0) { out << std::endl; }
1619  return n_out;
1620 }
1621 
1623 {
1624  int n_out = 0;
1625  for (const auto& n : maps->aliases)
1626  {
1627  const internal::Alias &alias = n.second;
1628  out << "\nalias: key " << n.first << ", "
1629  << "h_ptr " << alias.mem->h_ptr << ", "
1630  << "offset " << alias.offset << ", "
1631  << "counter " << alias.counter;
1632  n_out++;
1633  }
1634  if (maps->aliases.size() > 0) { out << std::endl; }
1635  return n_out;
1636 }
1637 
1638 int MemoryManager::CompareHostAndDevice_(void *h_ptr, size_t size,
1639  unsigned flags)
1640 {
1641  void *d_ptr = (flags & Mem::ALIAS) ?
1642  mm.GetAliasDevicePtr(h_ptr, size, false) :
1643  mm.GetDevicePtr(h_ptr, size, false);
1644  char *h_buf = new char[size];
1645 #ifdef MFEM_USE_CUDA
1646  CuMemcpyDtoH(h_buf, d_ptr, size);
1647 #elif MFE_USE_HIP
1648  HipMemcpyDtoH(h_buf, d_ptr, size);
1649 #else
1650  std::memcpy(h_buf, d_ptr, size);
1651 #endif
1652  int res = std::memcmp(h_ptr, h_buf, size);
1653  delete [] h_buf;
1654  return res;
1655 }
1656 
1657 
1658 void MemoryPrintFlags(unsigned flags)
1659 {
1660  typedef Memory<int> Mem;
1661  mfem::out
1662  << "\n registered = " << bool(flags & Mem::REGISTERED)
1663  << "\n owns host = " << bool(flags & Mem::OWNS_HOST)
1664  << "\n owns device = " << bool(flags & Mem::OWNS_DEVICE)
1665  << "\n owns internal = " << bool(flags & Mem::OWNS_INTERNAL)
1666  << "\n valid host = " << bool(flags & Mem::VALID_HOST)
1667  << "\n valid device = " << bool(flags & Mem::VALID_DEVICE)
1668  << "\n device flag = " << bool(flags & Mem::USE_DEVICE)
1669  << "\n alias = " << bool(flags & Mem::ALIAS)
1670  << std::endl;
1671 }
1672 
1673 void MemoryManager::CheckHostMemoryType_(MemoryType h_mt, void *h_ptr,
1674  bool alias)
1675 {
1676  if (!mm.exists) {return;}
1677  if (!alias)
1678  {
1679  auto it = maps->memories.find(h_ptr);
1680  MFEM_VERIFY(it != maps->memories.end(),
1681  "host pointer is not registered: h_ptr = " << h_ptr);
1682  MFEM_VERIFY(h_mt == it->second.h_mt, "host pointer MemoryType mismatch");
1683  }
1684  else
1685  {
1686  auto it = maps->aliases.find(h_ptr);
1687  MFEM_VERIFY(it != maps->aliases.end(),
1688  "alias pointer is not registered: h_ptr = " << h_ptr);
1689  MFEM_VERIFY(h_mt == it->second.h_mt, "alias pointer MemoryType mismatch");
1690  }
1691 }
1692 
1694 
1695 bool MemoryManager::exists = false;
1696 bool MemoryManager::configured = false;
1697 
1698 MemoryType MemoryManager::host_mem_type = MemoryType::HOST;
1699 MemoryType MemoryManager::device_mem_type = MemoryType::HOST;
1700 
1701 MemoryType MemoryManager::dual_map[MemoryTypeSize] =
1702 {
1703  /* HOST */ MemoryType::DEVICE,
1704  /* HOST_32 */ MemoryType::DEVICE,
1705  /* HOST_64 */ MemoryType::DEVICE,
1706  /* HOST_DEBUG */ MemoryType::DEVICE_DEBUG,
1707  /* HOST_UMPIRE */ MemoryType::DEVICE_UMPIRE,
1708  /* HOST_PINNED */ MemoryType::DEVICE,
1709  /* MANAGED */ MemoryType::MANAGED,
1710  /* DEVICE */ MemoryType::HOST,
1711  /* DEVICE_DEBUG */ MemoryType::HOST_DEBUG,
1712  /* DEVICE_UMPIRE */ MemoryType::HOST_UMPIRE,
1713  /* DEVICE_UMPIRE_2 */ MemoryType::HOST_UMPIRE
1714 };
1715 
1716 #ifdef MFEM_USE_UMPIRE
1717 const char * MemoryManager::h_umpire_name = "MFEM_HOST";
1718 const char * MemoryManager::d_umpire_name = "MFEM_DEVICE";
1719 const char * MemoryManager::d_umpire_2_name = "MFEM_DEVICE_2";
1720 #endif
1721 
1722 
1724 {
1725  "host-std", "host-32", "host-64", "host-debug", "host-umpire", "host-pinned",
1726 #if defined(MFEM_USE_CUDA)
1727  "cuda-uvm",
1728  "cuda",
1729 #elif defined(MFEM_USE_HIP)
1730  "hip-uvm",
1731  "hip",
1732 #else
1733  "managed",
1734  "device",
1735 #endif
1736  "device-debug",
1737 #if defined(MFEM_USE_CUDA)
1738  "cuda-umpire",
1739  "cuda-umpire-2",
1740 #elif defined(MFEM_USE_HIP)
1741  "hip-umpire",
1742  "hip-umpire-2",
1743 #else
1744  "device-umpire",
1745  "device-umpire-2",
1746 #endif
1747 };
1748 
1749 } // namespace mfem
void * CuMemcpyHtoD(void *dst, const void *src, size_t bytes)
Copies memory from Host to Device and returns destination ptr.
Definition: cuda.cpp:109
void * CuMemFree(void *dptr)
Frees device memory and returns destination ptr.
Definition: cuda.cpp:79
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
Device memory; using CUDA or HIP *Malloc and *Free.
void * CuMemFreeHostPinned(void *ptr)
Frees page-locked (pinned) host memory and returns destination ptr.
Definition: cuda.cpp:94
void PrintFlags() const
Print the internal flags.
static const char * GetUmpireHostAllocatorName()
Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE.
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
static MemoryType GetHostMemoryType()
Host pointer is valid.
Host memory; allocated from a &quot;host-debug&quot; pool.
void Configure(const MemoryType h_mt, const MemoryType d_mt)
Configure the Memory manager with given default host and device types. This method will be called whe...
Host memory: pinned (page-locked)
int PrintAliases(std::ostream &out=mfem::out)
int CompareHostAndDevice(int size) const
If both the host and the device data are valid, compare their contents.
bool IsAlias(const void *h_ptr)
Return true if the pointer is known by the memory manager as an alias.
bool MemoryClassContainsType(MemoryClass mc, MemoryType mt)
Return true iff the MemoryType mt is contained in the MemoryClass mc.
Definition: mem_manager.cpp:69
void * HipMemFreeHostPinned(void *ptr)
Frees page-locked (pinned) host memory and returns destination ptr.
Definition: hip.cpp:94
void * CuMallocManaged(void **dptr, size_t bytes)
Allocates managed device memory.
Definition: cuda.cpp:49
Host memory; aligned at 32 bytes.
static MemoryType GetDualMemoryType(MemoryType mt)
Return the dual MemoryType of the given one, mt.
constexpr int DeviceMemoryType
Definition: mem_manager.hpp:63
static const char * GetUmpireDeviceAllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE.
void * HipMemAllocHostPinned(void **ptr, size_t bytes)
Allocates page-locked (pinned) host memory.
Definition: hip.cpp:64
constexpr int HostMemoryType
Definition: mem_manager.hpp:61
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
Definition: error.cpp:153
double b
Definition: lissajous.cpp:42
static MemoryType GetDeviceMemoryType()
void * HipMemFree(void *dptr)
Frees device memory.
Definition: hip.cpp:79
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()
void * CuMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
Definition: cuda.cpp:132
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:60
void * HipMemcpyDtoH(void *dst, const void *src, size_t bytes)
Copies memory from Device to Host.
Definition: hip.cpp:155
static const char * GetUmpireDevice2AllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2.
void * HipMemAlloc(void **dptr, size_t bytes)
Allocates device memory.
Definition: hip.cpp:34
static void SetDualMemoryType(MemoryType mt, MemoryType dual_mt)
Set the dual memory type of mt to be dual_mt.
void Init()
Initialize the memory manager.
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:31
string space
bool IsKnown(const void *h_ptr)
Return true if the pointer is known by the memory manager.
constexpr int HostMemoryTypeSize
Definition: mem_manager.hpp:62
bool IsDeviceMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::DEVICE.
Definition: mem_manager.hpp:88
Pointer is an alias.
MemoryManager mm
The (single) global memory manager object.
double a
Definition: lissajous.cpp:41
Host memory; using new[] and delete[].
void * HipMemcpyHtoD(void *dst, const void *src, size_t bytes)
Copies memory from Host to Device.
Definition: hip.cpp:109
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
Definition: mem_manager.cpp:54
void * CuMemAllocHostPinned(void **ptr, size_t bytes)
Allocates page-locked (pinned) host memory.
Definition: cuda.cpp:64
void * HipMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
Definition: hip.cpp:132
constexpr int DeviceMemoryTypeSize
Definition: mem_manager.hpp:64
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.
void * CuMemAlloc(void **dptr, size_t bytes)
Allocates device memory and returns destination ptr.
Definition: cuda.cpp:34
MemoryClass
Memory classes identify sets of memory types.
Definition: mem_manager.hpp:73
void * CuMemcpyDtoH(void *dst, const void *src, size_t bytes)
Copies memory from Device to Host.
Definition: cuda.cpp:155
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().