MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
mem_manager.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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:
763 if (GetEnv("MFEM_MMU_STD")) { return new StdHostMemorySpace(); }
764 return new MmuHostMemorySpace();
765#ifdef MFEM_USE_UMPIRE
766 case MT::HOST_UMPIRE:
767 return new UmpireHostMemorySpace(
768 MemoryManager::GetUmpireHostAllocatorName());
769#else
770 case MT::HOST_UMPIRE: return new NoHostMemorySpace();
771#endif
772 case MT::HOST_PINNED: return new HostPinnedMemorySpace();
773 default: MFEM_ABORT("Unknown host memory controller!");
774 }
775 return nullptr;
776 }
777
778 DeviceMemorySpace* NewDeviceCtrl(const MemoryType mt)
779 {
780 switch (mt)
781 {
782#ifdef MFEM_USE_UMPIRE
783 case MT::DEVICE_UMPIRE:
784 return new UmpireDeviceMemorySpace(
785 MemoryManager::GetUmpireDeviceAllocatorName());
786 case MT::DEVICE_UMPIRE_2:
787 return new UmpireDeviceMemorySpace(
788 MemoryManager::GetUmpireDevice2AllocatorName());
789#else
790 case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
791 case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
792#endif
793 case MT::DEVICE_DEBUG:
794 if (GetEnv("MFEM_MMU_STD")) { return new StdDeviceMemorySpace(); }
795 return new MmuDeviceMemorySpace();
796 case MT::DEVICE:
797 {
798#if defined(MFEM_USE_CUDA)
799 return new CudaDeviceMemorySpace();
800#elif defined(MFEM_USE_HIP)
801 return new HipDeviceMemorySpace();
802#else
803 MFEM_ABORT("No device memory controller!");
804 break;
805#endif
806 }
807 default: MFEM_ABORT("Unknown device memory controller!");
808 }
809 return nullptr;
810 }
811};
812
813} // namespace mfem::internal
814
815static internal::Ctrl *ctrl;
816
817void *MemoryManager::New_(void *h_tmp, size_t bytes, MemoryType mt,
818 unsigned &flags)
819{
820 MFEM_ASSERT(exists, "Internal error!");
821 if (IsHostMemory(mt))
822 {
823 MFEM_ASSERT(mt != MemoryType::HOST && h_tmp == nullptr,
824 "Internal error!");
825 // d_mt = MemoryType::DEFAULT means d_mt = GetDualMemoryType(h_mt),
826 // evaluated at the time when the device pointer is allocated, see
827 // GetDevicePtr() and GetAliasDevicePtr()
828 const MemoryType d_mt = MemoryType::DEFAULT;
829 // We rely on the next call using lazy dev alloc
830 return New_(h_tmp, bytes, mt, d_mt, Mem::VALID_HOST, flags);
831 }
832 else
833 {
834 const MemoryType h_mt = GetDualMemoryType(mt);
835 return New_(h_tmp, bytes, h_mt, mt, Mem::VALID_DEVICE, flags);
836 }
837}
838
839void *MemoryManager::New_(void *h_tmp, size_t bytes, MemoryType h_mt,
840 MemoryType d_mt, unsigned valid_flags,
841 unsigned &flags)
842{
843 MFEM_ASSERT(exists, "Internal error!");
844 MFEM_ASSERT(IsHostMemory(h_mt), "h_mt must be host type");
845 MFEM_ASSERT(IsDeviceMemory(d_mt) || d_mt == h_mt ||
846 d_mt == MemoryType::DEFAULT,
847 "d_mt must be device type, the same is h_mt, or DEFAULT");
848 MFEM_ASSERT((h_mt != MemoryType::HOST || h_tmp != nullptr) &&
849 (h_mt == MemoryType::HOST || h_tmp == nullptr),
850 "Internal error");
851 MFEM_ASSERT((valid_flags & ~(Mem::VALID_HOST | Mem::VALID_DEVICE)) == 0,
852 "Internal error");
853 void *h_ptr;
854 if (h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
855 else { h_ptr = h_tmp; }
857 Mem::OWNS_DEVICE | valid_flags;
858 // The other New_() method relies on this lazy allocation behavior.
859 mm.Insert(h_ptr, bytes, h_mt, d_mt); // lazy dev alloc
860 // mm.InsertDevice(nullptr, h_ptr, bytes, h_mt, d_mt); // non-lazy dev alloc
861
862 // MFEM_VERIFY_TYPES(h_mt, mt); // done by mm.Insert() above
863 CheckHostMemoryType_(h_mt, h_ptr, false);
864
865 return h_ptr;
866}
867
868void *MemoryManager::Register_(void *ptr, void *h_tmp, size_t bytes,
869 MemoryType mt,
870 bool own, bool alias, unsigned &flags)
871{
872 MFEM_ASSERT(exists, "Internal error!");
873 const bool is_host_mem = IsHostMemory(mt);
874 const MemType h_mt = is_host_mem ? mt : GetDualMemoryType(mt);
875 const MemType d_mt = is_host_mem ? MemoryType::DEFAULT : mt;
876 // d_mt = MemoryType::DEFAULT means d_mt = GetDualMemoryType(h_mt),
877 // evaluated at the time when the device pointer is allocated, see
878 // GetDevicePtr() and GetAliasDevicePtr()
879
880 MFEM_VERIFY_TYPES(h_mt, d_mt);
881
882 if (ptr == nullptr && h_tmp == nullptr)
883 {
884 MFEM_VERIFY(bytes == 0, "internal error");
885 return nullptr;
886 }
887
888 MFEM_VERIFY(!alias, "Cannot register an alias!");
889
891 void *h_ptr;
892
893 if (is_host_mem) // HOST TYPES + MANAGED
894 {
895 h_ptr = ptr;
896 mm.Insert(h_ptr, bytes, h_mt, d_mt);
897 flags = (own ? flags | Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST) |
899 }
900 else // DEVICE TYPES
901 {
902 MFEM_VERIFY(ptr || bytes == 0,
903 "cannot register NULL device pointer with bytes = " << bytes);
904 if (h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
905 else { h_ptr = h_tmp; }
906 mm.InsertDevice(ptr, h_ptr, bytes, h_mt, d_mt);
907 flags = own ? flags | Mem::OWNS_DEVICE : flags & ~Mem::OWNS_DEVICE;
909 }
910 CheckHostMemoryType_(h_mt, h_ptr, alias);
911 return h_ptr;
912}
913
914void MemoryManager::Register2_(void *h_ptr, void *d_ptr, size_t bytes,
915 MemoryType h_mt, MemoryType d_mt,
916 bool own, bool alias, unsigned &flags,
917 unsigned valid_flags)
918{
919 MFEM_CONTRACT_VAR(alias);
920 MFEM_ASSERT(exists, "Internal error!");
921 MFEM_ASSERT(!alias, "Cannot register an alias!");
922 MFEM_VERIFY_TYPES(h_mt, d_mt);
923
924 if (h_ptr == nullptr && d_ptr == nullptr)
925 {
926 MFEM_VERIFY(bytes == 0, "internal error");
927 return;
928 }
929
931
932 MFEM_VERIFY(d_ptr || bytes == 0,
933 "cannot register NULL device pointer with bytes = " << bytes);
934 mm.InsertDevice(d_ptr, h_ptr, bytes, h_mt, d_mt);
935 flags = (own ? flags | (Mem::OWNS_HOST | Mem::OWNS_DEVICE) :
936 flags & ~(Mem::OWNS_HOST | Mem::OWNS_DEVICE)) |
937 valid_flags;
938
939 CheckHostMemoryType_(h_mt, h_ptr, alias);
940}
941
942void MemoryManager::Alias_(void *base_h_ptr, size_t offset, size_t bytes,
943 unsigned base_flags, unsigned &flags)
944{
945 mm.InsertAlias(base_h_ptr, (char*)base_h_ptr + offset, bytes,
946 base_flags & Mem::ALIAS);
947 flags = (base_flags | Mem::ALIAS) & ~(Mem::OWNS_HOST | Mem::OWNS_DEVICE);
948 if (base_h_ptr) { flags |= Mem::OWNS_INTERNAL; }
949}
950
951void MemoryManager::SetDeviceMemoryType_(void *h_ptr, unsigned flags,
952 MemoryType d_mt)
953{
954 MFEM_VERIFY(h_ptr, "cannot set the device memory type: Memory is empty!");
955 if (!(flags & Mem::ALIAS))
956 {
957 auto mem_iter = maps->memories.find(h_ptr);
958 MFEM_VERIFY(mem_iter != maps->memories.end(), "internal error");
959 internal::Memory &mem = mem_iter->second;
960 if (mem.d_mt == d_mt) { return; }
961 MFEM_VERIFY(mem.d_ptr == nullptr, "cannot set the device memory type:"
962 " device memory is allocated!");
963 mem.d_mt = d_mt;
964 }
965 else
966 {
967 auto alias_iter = maps->aliases.find(h_ptr);
968 MFEM_VERIFY(alias_iter != maps->aliases.end(), "internal error");
969 internal::Alias &alias = alias_iter->second;
970 internal::Memory &base_mem = *alias.mem;
971 if (base_mem.d_mt == d_mt) { return; }
972 MFEM_VERIFY(base_mem.d_ptr == nullptr,
973 "cannot set the device memory type:"
974 " alias' base device memory is allocated!");
975 base_mem.d_mt = d_mt;
976 }
977}
978
979void MemoryManager::Delete_(void *h_ptr, MemoryType h_mt, unsigned flags)
980{
981 const bool alias = flags & Mem::ALIAS;
982 const bool registered = flags & Mem::Registered;
983 const bool owns_host = flags & Mem::OWNS_HOST;
984 const bool owns_device = flags & Mem::OWNS_DEVICE;
985 const bool owns_internal = flags & Mem::OWNS_INTERNAL;
986 MFEM_ASSERT(IsHostMemory(h_mt), "invalid h_mt = " << (int)h_mt);
987 // MFEM_ASSERT(registered || IsHostMemory(h_mt),"");
988 MFEM_ASSERT(!owns_device || owns_internal, "invalid Memory state");
989 // If at least one of the 'own_*' flags is true then 'registered' must be
990 // true too. An acceptable exception is the special case when 'h_ptr' is
991 // NULL, and both 'own_device' and 'own_internal' are false -- this case is
992 // an exception only when 'own_host' is true and 'registered' is false.
993 MFEM_ASSERT(registered || !(owns_host || owns_device || owns_internal) ||
994 (!(owns_device || owns_internal) && h_ptr == nullptr),
995 "invalid Memory state");
996 if (!mm.exists || !registered) { return; }
997 if (alias)
998 {
999 if (owns_internal)
1000 {
1001 MFEM_ASSERT(mm.IsAlias(h_ptr), "");
1002 MFEM_ASSERT(h_mt == maps->aliases.at(h_ptr).h_mt, "");
1003 mm.EraseAlias(h_ptr);
1004 }
1005 }
1006 else // Known
1007 {
1008 if (owns_host && (h_mt != MemoryType::HOST))
1009 { ctrl->Host(h_mt)->Dealloc(h_ptr); }
1010 if (owns_internal)
1011 {
1012 MFEM_ASSERT(mm.IsKnown(h_ptr), "");
1013 MFEM_ASSERT(h_mt == maps->memories.at(h_ptr).h_mt, "");
1014 mm.Erase(h_ptr, owns_device);
1015 }
1016 }
1017}
1018
1019void MemoryManager::DeleteDevice_(void *h_ptr, unsigned & flags)
1020{
1021 const bool owns_device = flags & Mem::OWNS_DEVICE;
1022 if (owns_device)
1023 {
1024 mm.EraseDevice(h_ptr);
1025 flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
1026 }
1027}
1028
1029bool MemoryManager::MemoryClassCheck_(MemoryClass mc, void *h_ptr,
1030 MemoryType h_mt, size_t bytes,
1031 unsigned flags)
1032{
1033 if (!h_ptr)
1034 {
1035 MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
1036 return true;
1037 }
1038 MemoryType d_mt;
1039 if (!(flags & Mem::ALIAS))
1040 {
1041 auto iter = maps->memories.find(h_ptr);
1042 MFEM_VERIFY(iter != maps->memories.end(), "internal error");
1043 d_mt = iter->second.d_mt;
1044 }
1045 else
1046 {
1047 auto iter = maps->aliases.find(h_ptr);
1048 MFEM_VERIFY(iter != maps->aliases.end(), "internal error");
1049 d_mt = iter->second.mem->d_mt;
1050 }
1051 if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
1052 switch (mc)
1053 {
1055 {
1056 MFEM_VERIFY(h_mt == MemoryType::HOST_32 ||
1057 h_mt == MemoryType::HOST_64,"");
1058 return true;
1059 }
1061 {
1062 MFEM_VERIFY(h_mt == MemoryType::HOST_64,"");
1063 return true;
1064 }
1066 {
1067 MFEM_VERIFY(d_mt == MemoryType::DEVICE ||
1068 d_mt == MemoryType::DEVICE_DEBUG ||
1069 d_mt == MemoryType::DEVICE_UMPIRE ||
1071 d_mt == MemoryType::MANAGED,"");
1072 return true;
1073 }
1075 {
1076 MFEM_VERIFY((h_mt == MemoryType::MANAGED &&
1077 d_mt == MemoryType::MANAGED),"");
1078 return true;
1079 }
1080 default: break;
1081 }
1082 return true;
1083}
1084
1085void *MemoryManager::ReadWrite_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1086 size_t bytes, unsigned &flags)
1087{
1088 if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1089 if (bytes > 0) { MFEM_VERIFY(flags & Mem::Registered,""); }
1090 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1092 {
1093 const bool copy = !(flags & Mem::VALID_HOST);
1094 flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
1095 if (flags & Mem::ALIAS)
1096 { return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
1097 else { return mm.GetHostPtr(h_ptr, bytes, copy); }
1098 }
1099 else
1100 {
1101 const bool copy = !(flags & Mem::VALID_DEVICE);
1102 flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
1103 if (flags & Mem::ALIAS)
1104 { return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
1105 else { return mm.GetDevicePtr(h_ptr, bytes, copy); }
1106 }
1107}
1108
1109const void *MemoryManager::Read_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1110 size_t bytes, unsigned &flags)
1111{
1112 if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1113 if (bytes > 0) { MFEM_VERIFY(flags & Mem::Registered,""); }
1114 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1116 {
1117 const bool copy = !(flags & Mem::VALID_HOST);
1118 flags |= Mem::VALID_HOST;
1119 if (flags & Mem::ALIAS)
1120 { return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
1121 else { return mm.GetHostPtr(h_ptr, bytes, copy); }
1122 }
1123 else
1124 {
1125 const bool copy = !(flags & Mem::VALID_DEVICE);
1126 flags |= Mem::VALID_DEVICE;
1127 if (flags & Mem::ALIAS)
1128 { return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
1129 else { return mm.GetDevicePtr(h_ptr, bytes, copy); }
1130 }
1131}
1132
1133void *MemoryManager::Write_(void *h_ptr, MemoryType h_mt, MemoryClass mc,
1134 size_t bytes, unsigned &flags)
1135{
1136 if (h_ptr) { CheckHostMemoryType_(h_mt, h_ptr, flags & Mem::ALIAS); }
1137 if (bytes > 0) { MFEM_VERIFY(flags & Mem::Registered,""); }
1138 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),"");
1140 {
1141 flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
1142 if (flags & Mem::ALIAS)
1143 { return mm.GetAliasHostPtr(h_ptr, bytes, false); }
1144 else { return mm.GetHostPtr(h_ptr, bytes, false); }
1145 }
1146 else
1147 {
1148 flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
1149 if (flags & Mem::ALIAS)
1150 { return mm.GetAliasDevicePtr(h_ptr, bytes, false); }
1151 else { return mm.GetDevicePtr(h_ptr, bytes, false); }
1152 }
1153}
1154
1155void MemoryManager::SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
1156 size_t alias_bytes, unsigned base_flags,
1157 unsigned &alias_flags)
1158{
1159 // This is called only when (base_flags & Mem::Registered) is true.
1160 // Note that (alias_flags & Registered) may not be true.
1161 MFEM_ASSERT(alias_flags & Mem::ALIAS, "not an alias");
1162 if ((base_flags & Mem::VALID_HOST) && !(alias_flags & Mem::VALID_HOST))
1163 {
1164 mm.GetAliasHostPtr(alias_h_ptr, alias_bytes, true);
1165 }
1166 if ((base_flags & Mem::VALID_DEVICE) && !(alias_flags & Mem::VALID_DEVICE))
1167 {
1168 if (!(alias_flags & Mem::Registered))
1169 {
1170 mm.InsertAlias(base_h_ptr, alias_h_ptr, alias_bytes, base_flags & Mem::ALIAS);
1171 alias_flags = (alias_flags | Mem::Registered | Mem::OWNS_INTERNAL) &
1173 }
1174 mm.GetAliasDevicePtr(alias_h_ptr, alias_bytes, true);
1175 }
1176 alias_flags = (alias_flags & ~(Mem::VALID_HOST | Mem::VALID_DEVICE)) |
1177 (base_flags & (Mem::VALID_HOST | Mem::VALID_DEVICE));
1178}
1179
1180MemoryType MemoryManager::GetDeviceMemoryType_(void *h_ptr, bool alias)
1181{
1182 if (mm.exists)
1183 {
1184 if (!alias)
1185 {
1186 auto iter = maps->memories.find(h_ptr);
1187 MFEM_ASSERT(iter != maps->memories.end(), "internal error");
1188 return iter->second.d_mt;
1189 }
1190 // alias == true
1191 auto iter = maps->aliases.find(h_ptr);
1192 MFEM_ASSERT(iter != maps->aliases.end(), "internal error");
1193 return iter->second.mem->d_mt;
1194 }
1195 MFEM_ABORT("internal error");
1196 return MemoryManager::host_mem_type;
1197}
1198
1199MemoryType MemoryManager::GetHostMemoryType_(void *h_ptr)
1200{
1201 if (!mm.exists) { return MemoryManager::host_mem_type; }
1202 if (mm.IsKnown(h_ptr)) { return maps->memories.at(h_ptr).h_mt; }
1203 if (mm.IsAlias(h_ptr)) { return maps->aliases.at(h_ptr).h_mt; }
1204 return MemoryManager::host_mem_type;
1205}
1206
1207void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
1208 size_t bytes, unsigned src_flags,
1209 unsigned &dst_flags)
1210{
1211 // Type of copy to use based on the src and dest validity flags:
1212 // | src
1213 // | h | d | hd
1214 // -----------+-----+-----+------
1215 // h | h2h d2h h2h
1216 // dest d | h2d d2d d2d
1217 // hd | h2h d2d d2d
1218
1219 MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
1220 MFEM_ASSERT(dst_h_ptr != nullptr, "invalid dst_h_ptr = nullptr");
1221 MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
1222
1223 const bool dst_on_host =
1224 (dst_flags & Mem::VALID_HOST) &&
1225 (!(dst_flags & Mem::VALID_DEVICE) ||
1226 ((src_flags & Mem::VALID_HOST) && !(src_flags & Mem::VALID_DEVICE)));
1227
1228 dst_flags = dst_flags &
1229 ~(dst_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
1230
1231 const bool src_on_host =
1232 (src_flags & Mem::VALID_HOST) &&
1233 (!(src_flags & Mem::VALID_DEVICE) ||
1234 ((dst_flags & Mem::VALID_HOST) && !(dst_flags & Mem::VALID_DEVICE)));
1235
1236 const void *src_d_ptr =
1237 src_on_host ? NULL :
1238 ((src_flags & Mem::ALIAS) ?
1239 mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
1240 mm.GetDevicePtr(src_h_ptr, bytes, false));
1241
1242 if (dst_on_host)
1243 {
1244 if (src_on_host)
1245 {
1246 if (dst_h_ptr != src_h_ptr && bytes != 0)
1247 {
1248 MFEM_ASSERT((const char*)dst_h_ptr + bytes <= src_h_ptr ||
1249 (const char*)src_h_ptr + bytes <= dst_h_ptr,
1250 "data overlaps!");
1251 std::memcpy(dst_h_ptr, src_h_ptr, bytes);
1252 }
1253 }
1254 else
1255 {
1256 if (dst_h_ptr != src_d_ptr && bytes != 0)
1257 {
1258 MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
1259 maps->aliases.at(src_h_ptr).mem->d_mt :
1260 maps->memories.at(src_h_ptr).d_mt;
1261 ctrl->Device(src_d_mt)->DtoH(dst_h_ptr, src_d_ptr, bytes);
1262 }
1263 }
1264 }
1265 else
1266 {
1267 void *dest_d_ptr = (dst_flags & Mem::ALIAS) ?
1268 mm.GetAliasDevicePtr(dst_h_ptr, bytes, false) :
1269 mm.GetDevicePtr(dst_h_ptr, bytes, false);
1270 if (src_on_host)
1271 {
1272 const bool known = mm.IsKnown(dst_h_ptr);
1273 const bool alias = dst_flags & Mem::ALIAS;
1274 MFEM_VERIFY(alias||known,"");
1275 const MemoryType d_mt = known ?
1276 maps->memories.at(dst_h_ptr).d_mt :
1277 maps->aliases.at(dst_h_ptr).mem->d_mt;
1278 ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
1279 }
1280 else
1281 {
1282 if (dest_d_ptr != src_d_ptr && bytes != 0)
1283 {
1284 const bool known = mm.IsKnown(dst_h_ptr);
1285 const bool alias = dst_flags & Mem::ALIAS;
1286 MFEM_VERIFY(alias||known,"");
1287 const MemoryType d_mt = known ?
1288 maps->memories.at(dst_h_ptr).d_mt :
1289 maps->aliases.at(dst_h_ptr).mem->d_mt;
1290 ctrl->Device(d_mt)->DtoD(dest_d_ptr, src_d_ptr, bytes);
1291 }
1292 }
1293 }
1294}
1295
1296void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
1297 size_t bytes, unsigned src_flags)
1298{
1299 MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
1300 MFEM_ASSERT(dest_h_ptr != nullptr, "invalid dest_h_ptr = nullptr");
1301 MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
1302
1303 const bool src_on_host = src_flags & Mem::VALID_HOST;
1304 if (src_on_host)
1305 {
1306 if (dest_h_ptr != src_h_ptr && bytes != 0)
1307 {
1308 MFEM_ASSERT((char*)dest_h_ptr + bytes <= src_h_ptr ||
1309 (const char*)src_h_ptr + bytes <= dest_h_ptr,
1310 "data overlaps!");
1311 std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1312 }
1313 }
1314 else
1315 {
1316 MFEM_ASSERT(IsKnown_(src_h_ptr), "internal error");
1317 const void *src_d_ptr = (src_flags & Mem::ALIAS) ?
1318 mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
1319 mm.GetDevicePtr(src_h_ptr, bytes, false);
1320 MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
1321 maps->aliases.at(src_h_ptr).mem->d_mt :
1322 maps->memories.at(src_h_ptr).d_mt;
1323 ctrl->Device(src_d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
1324 }
1325}
1326
1327void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
1328 size_t bytes, unsigned &dest_flags)
1329{
1330 MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
1331 MFEM_ASSERT(dest_h_ptr != nullptr, "invalid dest_h_ptr = nullptr");
1332 MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
1333
1334 const bool dest_on_host = dest_flags & Mem::VALID_HOST;
1335 if (dest_on_host)
1336 {
1337 if (dest_h_ptr != src_h_ptr && bytes != 0)
1338 {
1339 MFEM_ASSERT((char*)dest_h_ptr + bytes <= src_h_ptr ||
1340 (const char*)src_h_ptr + bytes <= dest_h_ptr,
1341 "data overlaps!");
1342 std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1343 }
1344 }
1345 else
1346 {
1347 void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
1348 mm.GetAliasDevicePtr(dest_h_ptr, bytes, false) :
1349 mm.GetDevicePtr(dest_h_ptr, bytes, false);
1350 MemoryType dest_d_mt = (dest_flags & Mem::ALIAS) ?
1351 maps->aliases.at(dest_h_ptr).mem->d_mt :
1352 maps->memories.at(dest_h_ptr).d_mt;
1353 ctrl->Device(dest_d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
1354 }
1355 dest_flags = dest_flags &
1356 ~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
1357}
1358
1359bool MemoryManager::IsKnown_(const void *h_ptr)
1360{
1361 return maps->memories.find(h_ptr) != maps->memories.end();
1362}
1363
1364bool MemoryManager::IsAlias_(const void *h_ptr)
1365{
1366 return maps->aliases.find(h_ptr) != maps->aliases.end();
1367}
1368
1369void MemoryManager::Insert(void *h_ptr, size_t bytes,
1370 MemoryType h_mt, MemoryType d_mt)
1371{
1372#ifdef MFEM_TRACK_MEM_MANAGER
1373 mfem::out << "[mfem memory manager]: registering h_ptr: " << h_ptr
1374 << ", bytes: " << bytes << std::endl;
1375#endif
1376 if (h_ptr == NULL)
1377 {
1378 MFEM_VERIFY(bytes == 0, "Trying to add NULL with size " << bytes);
1379 return;
1380 }
1381 MFEM_VERIFY_TYPES(h_mt, d_mt);
1382#ifdef MFEM_DEBUG
1383 auto res =
1384#endif
1385 maps->memories.emplace(h_ptr, internal::Memory(h_ptr, bytes, h_mt, d_mt));
1386#ifdef MFEM_DEBUG
1387 if (res.second == false)
1388 {
1389 auto &m = res.first->second;
1390 MFEM_VERIFY(m.bytes >= bytes && m.h_mt == h_mt &&
1391 (m.d_mt == d_mt ||
1392 (d_mt == MemoryType::DEFAULT &&
1393 m.d_mt == GetDualMemoryType(h_mt)) ||
1394 (m.d_mt == MemoryType::DEFAULT &&
1395 d_mt == GetDualMemoryType(m.h_mt))),
1396 "Address already present with different attributes!");
1397#ifdef MFEM_TRACK_MEM_MANAGER
1398 mfem::out << "[mfem memory manager]: repeated registration of h_ptr: "
1399 << h_ptr << std::endl;
1400#endif
1401 }
1402#endif
1403}
1404
1405void MemoryManager::InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
1406 MemoryType h_mt, MemoryType d_mt)
1407{
1408 // MFEM_VERIFY_TYPES(h_mt, d_mt); // done by Insert() below
1409 MFEM_ASSERT(h_ptr != NULL, "internal error");
1410 Insert(h_ptr, bytes, h_mt, d_mt);
1411 internal::Memory &mem = maps->memories.at(h_ptr);
1412 if (d_ptr == NULL && bytes != 0) { ctrl->Device(d_mt)->Alloc(mem); }
1413 else { mem.d_ptr = d_ptr; }
1414}
1415
1416void MemoryManager::InsertAlias(const void *base_ptr, void *alias_ptr,
1417 const size_t bytes, const bool base_is_alias)
1418{
1419 size_t offset = static_cast<size_t>(static_cast<const char*>(alias_ptr) -
1420 static_cast<const char*>(base_ptr));
1421#ifdef MFEM_TRACK_MEM_MANAGER
1422 mfem::out << "[mfem memory manager]: registering alias of base_ptr: "
1423 << base_ptr << ", offset: " << offset << ", bytes: " << bytes
1424 << ", base is alias: " << base_is_alias << std::endl;
1425#endif
1426 if (!base_ptr)
1427 {
1428 MFEM_VERIFY(offset == 0,
1429 "Trying to add alias to NULL at offset " << offset);
1430 return;
1431 }
1432 if (base_is_alias)
1433 {
1434 const internal::Alias &alias = maps->aliases.at(base_ptr);
1435 MFEM_ASSERT(alias.mem,"");
1436 base_ptr = alias.mem->h_ptr;
1437 offset += alias.offset;
1438#ifdef MFEM_TRACK_MEM_MANAGER
1439 mfem::out << "[mfem memory manager]: real base_ptr: " << base_ptr
1440 << std::endl;
1441#endif
1442 }
1443 internal::Memory &mem = maps->memories.at(base_ptr);
1444 MFEM_VERIFY(offset + bytes <= mem.bytes, "invalid alias");
1445 auto res =
1446 maps->aliases.emplace(alias_ptr,
1447 internal::Alias{&mem, offset, 1, mem.h_mt});
1448 if (res.second == false) // alias_ptr was already in the map
1449 {
1450 internal::Alias &alias = res.first->second;
1451 // Update the alias data in case the existing alias is dangling
1452 alias.mem = &mem;
1453 alias.offset = offset;
1454 alias.h_mt = mem.h_mt;
1455 alias.counter++;
1456 }
1457}
1458
1459void MemoryManager::Erase(void *h_ptr, bool free_dev_ptr)
1460{
1461#ifdef MFEM_TRACK_MEM_MANAGER
1462 mfem::out << "[mfem memory manager]: un-registering h_ptr: " << h_ptr
1463 << std::endl;
1464#endif
1465 if (!h_ptr) { return; }
1466 auto mem_map_iter = maps->memories.find(h_ptr);
1467 if (mem_map_iter == maps->memories.end()) { mfem_error("Unknown pointer!"); }
1468 internal::Memory &mem = mem_map_iter->second;
1469 if (mem.d_ptr && free_dev_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem);}
1470 maps->memories.erase(mem_map_iter);
1471}
1472
1473void MemoryManager::EraseDevice(void *h_ptr)
1474{
1475 if (!h_ptr) { return; }
1476 auto mem_map_iter = maps->memories.find(h_ptr);
1477 if (mem_map_iter == maps->memories.end()) { mfem_error("Unknown pointer!"); }
1478 internal::Memory &mem = mem_map_iter->second;
1479 if (mem.d_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem);}
1480 mem.d_ptr = nullptr;
1481}
1482
1483void MemoryManager::EraseAlias(void *alias_ptr)
1484{
1485#ifdef MFEM_TRACK_MEM_MANAGER
1486 mfem::out << "[mfem memory manager]: un-registering alias_ptr: " << alias_ptr
1487 << std::endl;
1488#endif
1489 if (!alias_ptr) { return; }
1490 auto alias_map_iter = maps->aliases.find(alias_ptr);
1491 if (alias_map_iter == maps->aliases.end()) { mfem_error("Unknown alias!"); }
1492 internal::Alias &alias = alias_map_iter->second;
1493 if (--alias.counter) { return; }
1494 maps->aliases.erase(alias_map_iter);
1495}
1496
1497void *MemoryManager::GetDevicePtr(const void *h_ptr, size_t bytes,
1498 bool copy_data)
1499{
1500 if (!h_ptr)
1501 {
1502 MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
1503 return NULL;
1504 }
1505 internal::Memory &mem = maps->memories.at(h_ptr);
1506 const MemoryType &h_mt = mem.h_mt;
1507 MemoryType &d_mt = mem.d_mt;
1508 MFEM_VERIFY_TYPES(h_mt, d_mt);
1509 if (!mem.d_ptr)
1510 {
1511 if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
1512 if (mem.bytes) { ctrl->Device(d_mt)->Alloc(mem); }
1513 }
1514 // Aliases might have done some protections
1515 if (mem.d_ptr) { ctrl->Device(d_mt)->Unprotect(mem); }
1516 if (copy_data)
1517 {
1518 MFEM_ASSERT(bytes <= mem.bytes, "invalid copy size");
1519 if (bytes) { ctrl->Device(d_mt)->HtoD(mem.d_ptr, h_ptr, bytes); }
1520 }
1521 ctrl->Host(h_mt)->Protect(mem, bytes);
1522 return mem.d_ptr;
1523}
1524
1525void *MemoryManager::GetAliasDevicePtr(const void *alias_ptr, size_t bytes,
1526 bool copy)
1527{
1528 if (!alias_ptr)
1529 {
1530 MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
1531 return NULL;
1532 }
1533 auto &alias_map = maps->aliases;
1534 auto alias_map_iter = alias_map.find(alias_ptr);
1535 if (alias_map_iter == alias_map.end()) { mfem_error("alias not found"); }
1536 const internal::Alias &alias = alias_map_iter->second;
1537 const size_t offset = alias.offset;
1538 internal::Memory &mem = *alias.mem;
1539 const MemoryType &h_mt = mem.h_mt;
1540 MemoryType &d_mt = mem.d_mt;
1541 MFEM_VERIFY_TYPES(h_mt, d_mt);
1542 if (!mem.d_ptr)
1543 {
1544 if (d_mt == MemoryType::DEFAULT) { d_mt = GetDualMemoryType(h_mt); }
1545 if (mem.bytes) { ctrl->Device(d_mt)->Alloc(mem); }
1546 }
1547 void *alias_h_ptr = static_cast<char*>(mem.h_ptr) + offset;
1548 void *alias_d_ptr = static_cast<char*>(mem.d_ptr) + offset;
1549 MFEM_ASSERT(alias_h_ptr == alias_ptr, "internal error");
1550 MFEM_ASSERT(offset + bytes <= mem.bytes, "internal error");
1551 mem.d_rw = mem.h_rw = false;
1552 if (mem.d_ptr) { ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes); }
1553 ctrl->Host(h_mt)->AliasUnprotect(alias_ptr, bytes);
1554 if (copy && mem.d_ptr)
1555 { ctrl->Device(d_mt)->HtoD(alias_d_ptr, alias_h_ptr, bytes); }
1556 ctrl->Host(h_mt)->AliasProtect(alias_ptr, bytes);
1557 return alias_d_ptr;
1558}
1559
1560void *MemoryManager::GetHostPtr(const void *ptr, size_t bytes, bool copy)
1561{
1562 const internal::Memory &mem = maps->memories.at(ptr);
1563 MFEM_ASSERT(mem.h_ptr == ptr, "internal error");
1564 MFEM_ASSERT(bytes <= mem.bytes, "internal error")
1565 const MemoryType &h_mt = mem.h_mt;
1566 const MemoryType &d_mt = mem.d_mt;
1567 MFEM_VERIFY_TYPES(h_mt, d_mt);
1568 // Aliases might have done some protections
1569 ctrl->Host(h_mt)->Unprotect(mem, bytes);
1570 if (mem.d_ptr) { ctrl->Device(d_mt)->Unprotect(mem); }
1571 if (copy && mem.d_ptr) { ctrl->Device(d_mt)->DtoH(mem.h_ptr, mem.d_ptr, bytes); }
1572 if (mem.d_ptr) { ctrl->Device(d_mt)->Protect(mem); }
1573 return mem.h_ptr;
1574}
1575
1576void *MemoryManager::GetAliasHostPtr(const void *ptr, size_t bytes,
1577 bool copy_data)
1578{
1579 const internal::Alias &alias = maps->aliases.at(ptr);
1580 const internal::Memory *const mem = alias.mem;
1581 const MemoryType &h_mt = mem->h_mt;
1582 const MemoryType &d_mt = mem->d_mt;
1583 MFEM_VERIFY_TYPES(h_mt, d_mt);
1584 void *alias_h_ptr = static_cast<char*>(mem->h_ptr) + alias.offset;
1585 void *alias_d_ptr = static_cast<char*>(mem->d_ptr) + alias.offset;
1586 MFEM_ASSERT(alias_h_ptr == ptr, "internal error");
1587 mem->h_rw = false;
1588 ctrl->Host(h_mt)->AliasUnprotect(alias_h_ptr, bytes);
1589 if (mem->d_ptr) { ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes); }
1590 if (copy_data && mem->d_ptr)
1591 { ctrl->Device(d_mt)->DtoH(const_cast<void*>(ptr), alias_d_ptr, bytes); }
1592 if (mem->d_ptr) { ctrl->Device(d_mt)->AliasProtect(alias_d_ptr, bytes); }
1593 return alias_h_ptr;
1594}
1595
1597{
1598 if (exists) { return; }
1599 maps = new internal::Maps();
1600 ctrl = new internal::Ctrl();
1601 ctrl->Configure();
1602 exists = true;
1603}
1604
1606
1608
1610{
1611 MFEM_VERIFY(!configured, "changing the dual MemoryTypes is not allowed after"
1612 " MemoryManager configuration!");
1613 UpdateDualMemoryType(mt, dual_mt);
1614}
1615
1616void MemoryManager::UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt)
1617{
1618 MFEM_VERIFY((int)mt < MemoryTypeSize,
1619 "invalid MemoryType, mt = " << (int)mt);
1620 MFEM_VERIFY((int)dual_mt < MemoryTypeSize,
1621 "invalid dual MemoryType, dual_mt = " << (int)dual_mt);
1622
1623 if ((IsHostMemory(mt) && IsDeviceMemory(dual_mt)) ||
1624 (IsDeviceMemory(mt) && IsHostMemory(dual_mt)))
1625 {
1626 dual_map[(int)mt] = dual_mt;
1627 }
1628 else
1629 {
1630 // mt + dual_mt is not a pair of host + device types: this is only allowed
1631 // when mt == dual_mt and mt is a host type; in this case we do not
1632 // actually update the dual
1633 MFEM_VERIFY(mt == dual_mt && IsHostMemory(mt),
1634 "invalid (mt, dual_mt) pair: ("
1635 << MemoryTypeName[(int)mt] << ", "
1636 << MemoryTypeName[(int)dual_mt] << ')');
1637 }
1638}
1639
1641 const MemoryType device_mt)
1642{
1643 MemoryManager::UpdateDualMemoryType(host_mt, device_mt);
1644 MemoryManager::UpdateDualMemoryType(device_mt, host_mt);
1645 if (device_mt == MemoryType::DEVICE_DEBUG)
1646 {
1647 for (int mt = (int)MemoryType::HOST; mt < (int)MemoryType::MANAGED; mt++)
1648 {
1649 MemoryManager::UpdateDualMemoryType(
1651 }
1652 }
1653 Init();
1654 host_mem_type = host_mt;
1655 device_mem_type = device_mt;
1656 configured = true;
1657}
1658
1660{
1661 MFEM_VERIFY(exists, "MemoryManager has already been destroyed!");
1662#ifdef MFEM_TRACK_MEM_MANAGER
1663 size_t num_memories = maps->memories.size();
1664 size_t num_aliases = maps->aliases.size();
1665 if (num_memories != 0 || num_aliases != 0)
1666 {
1667 MFEM_WARNING("...\n\t number of registered pointers: " << num_memories
1668 << "\n\t number of registered aliases : " << num_aliases);
1669 }
1670#endif
1671 // Keep for debugging purposes:
1672#if 0
1673 mfem::out << "Destroying the MemoryManager ...\n"
1674 << "remaining registered pointers : "
1675 << maps->memories.size() << '\n'
1676 << "remaining registered aliases : "
1677 << maps->aliases.size() << '\n';
1678#endif
1679 for (auto& n : maps->memories)
1680 {
1681 internal::Memory &mem = n.second;
1682 bool mem_h_ptr = mem.h_mt != MemoryType::HOST && mem.h_ptr;
1683 if (mem_h_ptr) { ctrl->Host(mem.h_mt)->Dealloc(mem.h_ptr); }
1684 if (mem.d_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem); }
1685 }
1686 delete maps; maps = nullptr;
1687 delete ctrl; ctrl = nullptr;
1688 host_mem_type = MemoryType::HOST;
1689 device_mem_type = MemoryType::HOST;
1690 exists = false;
1691 configured = false;
1692}
1693
1695{
1696 if (ptr != NULL)
1697 {
1698 if (!IsKnown(ptr))
1699 {
1700 mfem_error("Pointer is not registered!");
1701 }
1702 }
1703}
1704
1705int MemoryManager::PrintPtrs(std::ostream &os)
1706{
1707 int n_out = 0;
1708 for (const auto& n : maps->memories)
1709 {
1710 const internal::Memory &mem = n.second;
1711 os << "\nkey " << n.first << ", "
1712 << "h_ptr " << mem.h_ptr << ", "
1713 << "d_ptr " << mem.d_ptr;
1714 n_out++;
1715 }
1716 if (maps->memories.size() > 0) { os << std::endl; }
1717 return n_out;
1718}
1719
1720int MemoryManager::PrintAliases(std::ostream &os)
1721{
1722 int n_out = 0;
1723 for (const auto& n : maps->aliases)
1724 {
1725 const internal::Alias &alias = n.second;
1726 os << "\nalias: key " << n.first << ", "
1727 << "h_ptr " << alias.mem->h_ptr << ", "
1728 << "offset " << alias.offset << ", "
1729 << "counter " << alias.counter;
1730 n_out++;
1731 }
1732 if (maps->aliases.size() > 0) { os << std::endl; }
1733 return n_out;
1734}
1735
1736int MemoryManager::CompareHostAndDevice_(void *h_ptr, size_t size,
1737 unsigned flags)
1738{
1739 void *d_ptr = (flags & Mem::ALIAS) ?
1740 mm.GetAliasDevicePtr(h_ptr, size, false) :
1741 mm.GetDevicePtr(h_ptr, size, false);
1742 char *h_buf = new char[size];
1743#if defined(MFEM_USE_CUDA)
1744 CuMemcpyDtoH(h_buf, d_ptr, size);
1745#elif defined(MFEM_USE_HIP)
1746 HipMemcpyDtoH(h_buf, d_ptr, size);
1747#else
1748 std::memcpy(h_buf, d_ptr, size);
1749#endif
1750 int res = std::memcmp(h_ptr, h_buf, size);
1751 delete [] h_buf;
1752 return res;
1753}
1754
1755
1756void MemoryPrintFlags(unsigned flags)
1757{
1758 typedef Memory<int> Mem;
1759 mfem::out
1760 << "\n registered = " << bool(flags & Mem::Registered)
1761 << "\n owns host = " << bool(flags & Mem::OWNS_HOST)
1762 << "\n owns device = " << bool(flags & Mem::OWNS_DEVICE)
1763 << "\n owns internal = " << bool(flags & Mem::OWNS_INTERNAL)
1764 << "\n valid host = " << bool(flags & Mem::VALID_HOST)
1765 << "\n valid device = " << bool(flags & Mem::VALID_DEVICE)
1766 << "\n device flag = " << bool(flags & Mem::USE_DEVICE)
1767 << "\n alias = " << bool(flags & Mem::ALIAS)
1768 << std::endl;
1769}
1770
1771void MemoryManager::CheckHostMemoryType_(MemoryType h_mt, void *h_ptr,
1772 bool alias)
1773{
1774 if (!mm.exists) {return;}
1775 if (!alias)
1776 {
1777 auto it = maps->memories.find(h_ptr);
1778 MFEM_VERIFY(it != maps->memories.end(),
1779 "host pointer is not registered: h_ptr = " << h_ptr);
1780 MFEM_VERIFY(h_mt == it->second.h_mt, "host pointer MemoryType mismatch");
1781 }
1782 else
1783 {
1784 auto it = maps->aliases.find(h_ptr);
1785 MFEM_VERIFY(it != maps->aliases.end(),
1786 "alias pointer is not registered: h_ptr = " << h_ptr);
1787 MFEM_VERIFY(h_mt == it->second.h_mt, "alias pointer MemoryType mismatch");
1788 }
1789}
1790
1792
1793bool MemoryManager::exists = false;
1794bool MemoryManager::configured = false;
1795
1796MemoryType MemoryManager::host_mem_type = MemoryType::HOST;
1797MemoryType MemoryManager::device_mem_type = MemoryType::HOST;
1798
1799MemoryType MemoryManager::dual_map[MemoryTypeSize] =
1800{
1801 /* HOST */ MemoryType::DEVICE,
1802 /* HOST_32 */ MemoryType::DEVICE,
1803 /* HOST_64 */ MemoryType::DEVICE,
1804 /* HOST_DEBUG */ MemoryType::DEVICE_DEBUG,
1805 /* HOST_UMPIRE */ MemoryType::DEVICE_UMPIRE,
1806 /* HOST_PINNED */ MemoryType::DEVICE,
1807 /* MANAGED */ MemoryType::MANAGED,
1808 /* DEVICE */ MemoryType::HOST,
1809 /* DEVICE_DEBUG */ MemoryType::HOST_DEBUG,
1810 /* DEVICE_UMPIRE */ MemoryType::HOST_UMPIRE,
1811 /* DEVICE_UMPIRE_2 */ MemoryType::HOST_UMPIRE
1812};
1813
1814#ifdef MFEM_USE_UMPIRE
1815const char * MemoryManager::h_umpire_name = "MFEM_HOST";
1816const char * MemoryManager::d_umpire_name = "MFEM_DEVICE";
1817const char * MemoryManager::d_umpire_2_name = "MFEM_DEVICE_2";
1818#endif
1819
1820
1822{
1823 "host-std", "host-32", "host-64", "host-debug", "host-umpire", "host-pinned",
1824#if defined(MFEM_USE_CUDA)
1825 "cuda-uvm",
1826 "cuda",
1827#elif defined(MFEM_USE_HIP)
1828 "hip-uvm",
1829 "hip",
1830#else
1831 "managed",
1832 "device",
1833#endif
1834 "device-debug",
1835#if defined(MFEM_USE_CUDA)
1836 "cuda-umpire",
1837 "cuda-umpire-2",
1838#elif defined(MFEM_USE_HIP)
1839 "hip-umpire",
1840 "hip-umpire-2",
1841#else
1842 "device-umpire",
1843 "device-umpire-2",
1844#endif
1845};
1846
1847} // 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:2104
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
if(k >=N)
Definition forall.hpp:738
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)