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