17 #include <unordered_map>
28 #define mfem_memalign(p,a,s) posix_memalign(p,a,s)
30 #define mfem_memalign(p,a,s) (((*(p))=_aligned_malloc((s),(a))),*(p)?0:errno)
33 #ifdef MFEM_USE_UMPIRE
34 #include "umpire/Umpire.hpp"
37 #if defined(MFEM_USE_CUDA) && !defined(UMPIRE_ENABLE_CUDA)
38 #error "CUDA is not enabled in Umpire!"
41 #if defined(MFEM_USE_HIP) && !defined(UMPIRE_ENABLE_HIP)
42 #error "HIP is not enabled in Umpire!"
44 #endif // MFEM_USE_UMPIRE
59 MFEM_VERIFY(
false,
"");
79 MFEM_VERIFY(
false,
"");
94 MFEM_VERIFY(sync,
"");
111 return std::max(mc1, mc2);
138 h_ptr(p), d_ptr(nullptr), bytes(b), h_mt(h), d_mt(d) { }
145 const size_t offset, bytes;
151 typedef std::unordered_map<const void*, Memory> MemoryMap;
152 typedef std::unordered_map<const void*, Alias> AliasMap;
162 static internal::Maps *maps;
168 class HostMemorySpace
171 virtual ~HostMemorySpace() { }
172 virtual void Alloc(
void **ptr,
size_t bytes) { *ptr = std::malloc(bytes); }
173 virtual void Dealloc(
void *ptr) { std::free(ptr); }
174 virtual void Protect(
const void*,
size_t) { }
175 virtual void Unprotect(
const void*,
size_t) { }
176 virtual void AliasProtect(
const void*,
size_t) { }
177 virtual void AliasUnprotect(
const void*,
size_t) { }
181 class DeviceMemorySpace
184 virtual ~DeviceMemorySpace() { }
185 virtual void Alloc(Memory &base) { base.d_ptr = std::malloc(base.bytes); }
186 virtual void Dealloc(Memory &base) { std::free(base.d_ptr); }
187 virtual void Protect(
const Memory&) { }
188 virtual void Unprotect(
const Memory&) { }
189 virtual void AliasProtect(
const void*,
size_t) { }
190 virtual void AliasUnprotect(
const void*,
size_t) { }
191 virtual void *HtoD(
void *dst,
const void *src,
size_t bytes)
192 {
return std::memcpy(dst, src, bytes); }
193 virtual void *DtoD(
void *dst,
const void *src,
size_t bytes)
194 {
return std::memcpy(dst, src, bytes); }
195 virtual void *DtoH(
void *dst,
const void *src,
size_t bytes)
196 {
return std::memcpy(dst, src, bytes); }
200 class StdHostMemorySpace :
public HostMemorySpace { };
203 struct NoHostMemorySpace :
public HostMemorySpace
205 void Alloc(
void**,
const size_t) {
mfem_error(
"! Host Alloc error"); }
209 class Aligned32HostMemorySpace :
public HostMemorySpace
212 Aligned32HostMemorySpace(): HostMemorySpace() { }
213 void Alloc(
void **ptr,
size_t bytes)
214 {
if (mfem_memalign(ptr, 32, bytes) != 0) { throw ::std::bad_alloc(); } }
215 void Dealloc(
void *ptr) { std::free(ptr); }
219 class Aligned64HostMemorySpace :
public HostMemorySpace
222 Aligned64HostMemorySpace(): HostMemorySpace() { }
223 void Alloc(
void **ptr,
size_t bytes)
224 {
if (mfem_memalign(ptr, 64, bytes) != 0) { throw ::std::bad_alloc(); } }
228 static uintptr_t pagesize = 0;
229 static uintptr_t pagemask = 0;
232 inline const void *MmuAddrR(
const void *ptr)
234 const uintptr_t addr = (uintptr_t) ptr;
235 return (addr & pagemask) ? (
void*) ((addr + pagesize) & ~pagemask) : ptr;
239 inline const void *MmuAddrP(
const void *ptr)
241 const uintptr_t addr = (uintptr_t) ptr;
242 return (
void*) (addr & ~pagemask);
246 inline uintptr_t MmuLengthR(
const void *ptr,
const size_t bytes)
249 const uintptr_t
a = (uintptr_t) ptr;
250 const uintptr_t A = (uintptr_t) MmuAddrR(ptr);
251 MFEM_ASSERT(a <= A,
"");
252 const uintptr_t
b = a + bytes;
253 const uintptr_t B = b & ~pagemask;
254 MFEM_ASSERT(B <= b,
"");
255 const uintptr_t length = B > A ? B - A : 0;
256 MFEM_ASSERT(length % pagesize == 0,
"");
261 inline uintptr_t MmuLengthP(
const void *ptr,
const size_t bytes)
264 const uintptr_t a = (uintptr_t) ptr;
265 const uintptr_t A = (uintptr_t) MmuAddrP(ptr);
266 MFEM_ASSERT(A <= a,
"");
267 const uintptr_t
b = a + bytes;
268 const uintptr_t B = b & pagemask ? (b + pagesize) & ~pagemask : b;
269 MFEM_ASSERT(b <= B,
"");
270 MFEM_ASSERT(B >= A,
"");
271 const uintptr_t length = B - A;
272 MFEM_ASSERT(length % pagesize == 0,
"");
277 static void MmuError(
int, siginfo_t *si,
void*)
281 const void *ptr = si->si_addr;
282 sprintf(str,
"Error while accessing address %p!", ptr);
283 mfem::out << std::endl <<
"An illegal memory access was made!";
288 static void MmuInit()
290 if (pagesize > 0) {
return; }
292 sa.sa_flags = SA_SIGINFO;
293 sigemptyset(&sa.sa_mask);
294 sa.sa_sigaction = MmuError;
295 if (sigaction(SIGBUS, &sa, NULL) == -1) {
mfem_error(
"SIGBUS"); }
296 if (sigaction(SIGSEGV, &sa, NULL) == -1) {
mfem_error(
"SIGSEGV"); }
297 pagesize = (uintptr_t) sysconf(_SC_PAGE_SIZE);
298 MFEM_ASSERT(pagesize > 0,
"pagesize must not be less than 1");
299 pagemask = pagesize - 1;
303 inline void MmuAlloc(
void **ptr,
const size_t bytes)
305 const size_t length = bytes == 0 ? 8 : bytes;
306 const int prot = PROT_READ | PROT_WRITE;
307 const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
308 *ptr = ::mmap(NULL, length, prot, flags, -1, 0);
309 if (*ptr == MAP_FAILED) { throw ::std::bad_alloc(); }
313 inline void MmuDealloc(
void *ptr,
const size_t bytes)
315 const size_t length = bytes == 0 ? 8 : bytes;
316 if (::munmap(ptr, length) == -1) {
mfem_error(
"Dealloc error!"); }
320 inline void MmuProtect(
const void *ptr,
const size_t bytes)
322 if (!::mprotect(const_cast<void*>(ptr), bytes, PROT_NONE)) {
return; }
327 inline void MmuAllow(
const void *ptr,
const size_t bytes)
329 const int RW = PROT_READ | PROT_WRITE;
330 if (!::mprotect(const_cast<void*>(ptr), bytes, RW)) {
return; }
334 inline void MmuInit() { }
335 inline void MmuAlloc(
void **ptr,
const size_t bytes) { *ptr = std::malloc(bytes); }
336 inline void MmuDealloc(
void *ptr,
const size_t) { std::free(ptr); }
337 inline void MmuProtect(
const void*,
const size_t) { }
338 inline void MmuAllow(
const void*,
const size_t) { }
339 inline const void *MmuAddrR(
const void *a) {
return a; }
340 inline const void *MmuAddrP(
const void *a) {
return a; }
341 inline uintptr_t MmuLengthR(
const void*,
const size_t) {
return 0; }
342 inline uintptr_t MmuLengthP(
const void*,
const size_t) {
return 0; }
346 class MmuHostMemorySpace :
public HostMemorySpace
349 MmuHostMemorySpace(): HostMemorySpace() { MmuInit(); }
350 void Alloc(
void **ptr,
size_t bytes) { MmuAlloc(ptr, bytes); }
351 void Dealloc(
void *ptr) { MmuDealloc(ptr, maps->memories.at(ptr).bytes); }
352 void Protect(
const void *ptr,
size_t bytes) { MmuProtect(ptr, bytes); }
353 void Unprotect(
const void *ptr,
size_t bytes) { MmuAllow(ptr, bytes); }
355 void AliasProtect(
const void *ptr,
size_t bytes)
356 { MmuProtect(MmuAddrR(ptr), MmuLengthR(ptr, bytes)); }
358 void AliasUnprotect(
const void *ptr,
size_t bytes)
359 { MmuAllow(MmuAddrP(ptr), MmuLengthP(ptr, bytes)); }
363 class UvmHostMemorySpace :
public HostMemorySpace
366 UvmHostMemorySpace(): HostMemorySpace() { }
367 void Alloc(
void **ptr,
size_t bytes) {
CuMallocManaged(ptr, bytes == 0 ? 8 : bytes); }
368 void Dealloc(
void *ptr) {
CuMemFree(ptr); }
372 class NoDeviceMemorySpace:
public DeviceMemorySpace
375 void Alloc(internal::Memory&) {
mfem_error(
"! Device Alloc"); }
376 void Dealloc(Memory&) {
mfem_error(
"! Device Dealloc"); }
377 void *HtoD(
void*,
const void*,
size_t) {
mfem_error(
"!HtoD");
return nullptr; }
378 void *DtoD(
void*,
const void*,
size_t) {
mfem_error(
"!DtoD");
return nullptr; }
379 void *DtoH(
void*,
const void*,
size_t) {
mfem_error(
"!DtoH");
return nullptr; }
383 class StdDeviceMemorySpace :
public DeviceMemorySpace { };
386 class CudaDeviceMemorySpace:
public DeviceMemorySpace
389 CudaDeviceMemorySpace(): DeviceMemorySpace() { }
390 void Alloc(Memory &base) {
CuMemAlloc(&base.d_ptr, base.bytes); }
391 void Dealloc(Memory &base) {
CuMemFree(base.d_ptr); }
392 void *HtoD(
void *dst,
const void *src,
size_t bytes)
394 void *DtoD(
void* dst,
const void* src,
size_t bytes)
396 void *DtoH(
void *dst,
const void *src,
size_t bytes)
401 class HipDeviceMemorySpace:
public DeviceMemorySpace
404 HipDeviceMemorySpace(): DeviceMemorySpace() { }
405 void Alloc(Memory &base) {
HipMemAlloc(&base.d_ptr, base.bytes); }
406 void Dealloc(Memory &base) {
HipMemFree(base.d_ptr); }
407 void *HtoD(
void *dst,
const void *src,
size_t bytes)
409 void *DtoD(
void* dst,
const void* src,
size_t bytes)
411 void *DtoH(
void *dst,
const void *src,
size_t bytes)
416 class UvmCudaMemorySpace :
public DeviceMemorySpace
419 void Alloc(Memory &base) { base.d_ptr = base.h_ptr; }
420 void Dealloc(Memory&) { }
421 void *HtoD(
void *dst,
const void *src,
size_t bytes)
423 if (dst == src) { MFEM_STREAM_SYNC;
return dst; }
426 void *DtoD(
void* dst,
const void* src,
size_t bytes)
428 void *DtoH(
void *dst,
const void *src,
size_t bytes)
430 if (dst == src) { MFEM_STREAM_SYNC;
return dst; }
436 class MmuDeviceMemorySpace :
public DeviceMemorySpace
439 MmuDeviceMemorySpace(): DeviceMemorySpace() { }
440 void Alloc(Memory &m) { MmuAlloc(&m.d_ptr, m.bytes); }
441 void Dealloc(Memory &m) { MmuDealloc(m.d_ptr, m.bytes); }
442 void Protect(
const Memory &m) { MmuProtect(m.d_ptr, m.bytes); }
443 void Unprotect(
const Memory &m) { MmuAllow(m.d_ptr, m.bytes); }
445 void AliasProtect(
const void *ptr,
size_t bytes)
446 { MmuProtect(MmuAddrR(ptr), MmuLengthR(ptr, bytes)); }
448 void AliasUnprotect(
const void *ptr,
size_t bytes)
449 { MmuAllow(MmuAddrP(ptr), MmuLengthP(ptr, bytes)); }
450 void *HtoD(
void *dst,
const void *src,
size_t bytes)
451 {
return std::memcpy(dst, src, bytes); }
452 void *DtoD(
void *dst,
const void *src,
size_t bytes)
453 {
return std::memcpy(dst, src, bytes); }
454 void *DtoH(
void *dst,
const void *src,
size_t bytes)
455 {
return std::memcpy(dst, src, bytes); }
458 #ifndef MFEM_USE_UMPIRE
459 class UmpireHostMemorySpace :
public NoHostMemorySpace { };
460 class UmpireDeviceMemorySpace :
public NoDeviceMemorySpace { };
463 class UmpireHostMemorySpace :
public HostMemorySpace
467 umpire::ResourceManager &rm;
468 umpire::Allocator h_allocator;
469 umpire::strategy::AllocationStrategy *strat;
471 ~UmpireHostMemorySpace() { h_allocator.release(); }
472 UmpireHostMemorySpace():
474 name(
mm.GetUmpireAllocatorHostName()),
475 rm(umpire::ResourceManager::getInstance()),
476 h_allocator(rm.isAllocator(name)? rm.getAllocator(name):
477 rm.makeAllocator<umpire::strategy::DynamicPool>
478 (name, rm.getAllocator(
"HOST"))),
479 strat(h_allocator.getAllocationStrategy()) { }
480 void Alloc(
void **ptr,
size_t bytes) { *ptr = h_allocator.allocate(bytes); }
481 void Dealloc(
void *ptr) { h_allocator.deallocate(ptr); }
482 void Insert(
void *ptr,
size_t bytes)
483 { rm.registerAllocation(ptr, {ptr, bytes, strat}); }
488 class UmpireDeviceMemorySpace :
public DeviceMemorySpace
492 umpire::ResourceManager &rm;
493 umpire::Allocator d_allocator;
495 ~UmpireDeviceMemorySpace() { d_allocator.release(); }
496 UmpireDeviceMemorySpace():
498 name(
mm.GetUmpireAllocatorDeviceName()),
499 rm(umpire::ResourceManager::getInstance()),
500 d_allocator(rm.isAllocator(name)? rm.getAllocator(name):
501 rm.makeAllocator<umpire::strategy::DynamicPool>
502 (name, rm.getAllocator(
"DEVICE"))) { }
503 void Alloc(Memory &base) { base.d_ptr = d_allocator.allocate(base.bytes); }
504 void Dealloc(Memory &base) { d_allocator.deallocate(base.d_ptr); }
505 void *HtoD(
void *dst,
const void *src,
size_t bytes)
515 void *DtoD(
void* dst,
const void* src,
size_t bytes)
525 void *DtoH(
void *dst,
const void *src,
size_t bytes)
537 class UmpireDeviceMemorySpace :
public NoDeviceMemorySpace { };
538 #endif // MFEM_USE_CUDA
539 #endif // MFEM_USE_UMPIRE
551 Ctrl(): host{
nullptr}, device{
nullptr} { }
557 mfem_error(
"Memory backends have already been configured!");
563 host[
static_cast<int>(
MT::HOST)] =
new StdHostMemorySpace();
564 host[
static_cast<int>(
MT::HOST_32)] =
new Aligned32HostMemorySpace();
565 host[
static_cast<int>(
MT::HOST_64)] =
new Aligned64HostMemorySpace();
569 host[
static_cast<int>(
MT::MANAGED)] =
new UvmHostMemorySpace();
573 device[
static_cast<int>(
MT::MANAGED)-shift] =
new UvmCudaMemorySpace();
582 const int mt_i =
static_cast<int>(mt);
584 if (!host[mt_i]) { host[mt_i] = NewHostCtrl(mt); }
585 MFEM_ASSERT(host[mt_i],
"Host memory controller is not configured!");
589 DeviceMemorySpace* Device(
const MemoryType mt)
592 MFEM_ASSERT(mt_i >= 0,
"");
594 if (!device[mt_i]) { device[mt_i] = NewDeviceCtrl(mt); }
595 MFEM_ASSERT(device[mt_i],
"Memory manager has not been configured!");
604 for (
int mt = mt_d; mt <
MemoryTypeSize; mt++) {
delete device[mt-mt_d]; }
608 HostMemorySpace* NewHostCtrl(
const MemoryType mt)
611 MFEM_ABORT(
"Unknown host memory controller!");
615 DeviceMemorySpace* NewDeviceCtrl(
const MemoryType mt)
623 #if defined(MFEM_USE_CUDA)
624 return new CudaDeviceMemorySpace();
625 #elif defined(MFEM_USE_HIP)
626 return new HipDeviceMemorySpace();
628 MFEM_ABORT(
"No device memory controller!");
632 default: MFEM_ABORT(
"Unknown device memory controller!");
640 static internal::Ctrl *ctrl;
642 void *MemoryManager::New_(
void *h_tmp,
size_t bytes,
MemoryType mt,
645 MFEM_ASSERT(exists,
"Internal error!");
648 const MemType dual_mt = GetDualMemoryType_(mt);
649 const MemType h_mt = is_host_mem ? mt : dual_mt;
650 const MemType d_mt = is_host_mem ? dual_mt : mt;
651 MFEM_VERIFY_TYPES(h_mt, d_mt);
653 if (h_tmp ==
nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
657 if (is_host_mem) {
mm.Insert(h_ptr, bytes, h_mt, d_mt); }
658 else {
mm.InsertDevice(
nullptr, h_ptr, bytes, h_mt, d_mt); }
659 CheckHostMemoryType_(h_mt, h_ptr);
663 void *MemoryManager::Register_(
void *ptr,
void *h_tmp,
size_t bytes,
665 bool own,
bool alias,
unsigned &flags)
667 MFEM_CONTRACT_VAR(alias);
668 MFEM_ASSERT(exists,
"Internal error!");
670 MFEM_ASSERT(!alias,
"Cannot register an alias!");
672 const MemType dual_mt = GetDualMemoryType_(mt);
673 const MemType h_mt = mt;
674 const MemType d_mt = dual_mt;
675 MFEM_VERIFY_TYPES(h_mt, d_mt);
677 if (ptr ==
nullptr && h_tmp ==
nullptr)
679 MFEM_VERIFY(bytes == 0,
"internal error");
689 mm.Insert(h_ptr, bytes, h_mt, d_mt);
690 flags = (own ? flags |
Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST) |
696 if (h_tmp ==
nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
697 mm.InsertDevice(ptr, h_ptr, bytes, h_mt, d_mt);
701 CheckHostMemoryType_(h_mt, h_ptr);
705 void MemoryManager::Alias_(
void *base_h_ptr,
size_t offset,
size_t bytes,
706 unsigned base_flags,
unsigned &flags)
708 mm.InsertAlias(base_h_ptr, (
char*)base_h_ptr + offset, bytes,
722 MFEM_ASSERT(!owns_device || owns_internal,
"invalid Memory state");
723 if (!
mm.exists || !registered) {
return mt; }
728 const MemoryType h_mt = maps->aliases.at(h_ptr).h_mt;
729 MFEM_ASSERT(mt == h_mt,
"");
730 mm.EraseAlias(h_ptr);
737 MFEM_ASSERT(!owns_internal ||
738 mt == maps->memories.at(h_ptr).h_mt,
"");
740 { ctrl->Host(h_mt)->Dealloc(h_ptr); }
741 if (owns_internal) {
mm.Erase(h_ptr, owns_device); }
747 bool MemoryManager::MemoryClassCheck_(
MemoryClass mc,
void *h_ptr,
753 MFEM_VERIFY(bytes == 0,
"Trying to access NULL with size " << bytes);
759 const bool check = known || ((flags &
Mem::ALIAS) && alias);
760 MFEM_VERIFY(check,
"");
761 const internal::Memory &mem =
763 *maps->aliases.at(h_ptr).mem : maps->memories.at(h_ptr);
798 size_t bytes,
unsigned &flags)
800 MemoryManager::CheckHostMemoryType_(h_mt, h_ptr);
801 if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,
""); }
802 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),
"");
807 if (flags & Mem::ALIAS)
808 {
return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
809 else {
return mm.GetHostPtr(h_ptr, bytes, copy); }
815 if (flags & Mem::ALIAS)
816 {
return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
817 else {
return mm.GetDevicePtr(h_ptr, bytes, copy); }
822 size_t bytes,
unsigned &flags)
824 CheckHostMemoryType_(h_mt, h_ptr);
825 if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,
""); }
826 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),
"");
831 if (flags & Mem::ALIAS)
832 {
return mm.GetAliasHostPtr(h_ptr, bytes, copy); }
833 else {
return mm.GetHostPtr(h_ptr, bytes, copy); }
839 if (flags & Mem::ALIAS)
840 {
return mm.GetAliasDevicePtr(h_ptr, bytes, copy); }
841 else {
return mm.GetDevicePtr(h_ptr, bytes, copy); }
846 size_t bytes,
unsigned &flags)
848 CheckHostMemoryType_(h_mt, h_ptr);
849 if (bytes > 0) { MFEM_VERIFY(flags & Mem::REGISTERED,
""); }
850 MFEM_ASSERT(MemoryClassCheck_(mc, h_ptr, h_mt, bytes, flags),
"");
854 if (flags & Mem::ALIAS)
855 {
return mm.GetAliasHostPtr(h_ptr, bytes,
false); }
856 else {
return mm.GetHostPtr(h_ptr, bytes,
false); }
861 if (flags & Mem::ALIAS)
862 {
return mm.GetAliasDevicePtr(h_ptr, bytes,
false); }
863 else {
return mm.GetDevicePtr(h_ptr, bytes,
false); }
868 void MemoryManager::SyncAlias_(
const void *base_h_ptr,
void *alias_h_ptr,
869 size_t alias_bytes,
unsigned base_flags,
870 unsigned &alias_flags)
874 MFEM_ASSERT(alias_flags & Mem::ALIAS,
"not an alias");
875 if ((base_flags &
Mem::VALID_HOST) && !(alias_flags & Mem::VALID_HOST))
877 mm.GetAliasHostPtr(alias_h_ptr, alias_bytes,
true);
881 if (!(alias_flags & Mem::REGISTERED))
883 mm.InsertAlias(base_h_ptr, alias_h_ptr, alias_bytes, base_flags & Mem::ALIAS);
885 ~(Mem::OWNS_HOST | Mem::OWNS_DEVICE);
887 mm.GetAliasDevicePtr(alias_h_ptr, alias_bytes,
true);
893 MemoryType MemoryManager::GetDeviceMemoryType_(
void *h_ptr)
900 internal::Memory &mem = maps->memories.at(h_ptr);
906 internal::Memory *mem = maps->aliases.at(h_ptr).mem;
910 MFEM_ABORT(
"internal error");
911 return MemoryManager::host_mem_type;
914 MemoryType MemoryManager::GetHostMemoryType_(
void *h_ptr)
916 if (!
mm.exists) {
return MemoryManager::host_mem_type; }
917 if (
mm.
IsKnown(h_ptr)) {
return maps->memories.at(h_ptr).h_mt; }
918 if (
mm.
IsAlias(h_ptr)) {
return maps->aliases.at(h_ptr).mem->h_mt; }
919 return MemoryManager::host_mem_type;
922 void MemoryManager::Copy_(
void *dst_h_ptr,
const void *src_h_ptr,
923 size_t bytes,
unsigned src_flags,
934 const bool dst_on_host =
936 (!(dst_flags & Mem::VALID_DEVICE) ||
939 dst_flags = dst_flags &
942 const bool src_on_host =
944 (!(src_flags & Mem::VALID_DEVICE) ||
947 const void *src_d_ptr =
950 mm.GetAliasDevicePtr(src_h_ptr, bytes,
false) :
951 mm.GetDevicePtr(src_h_ptr, bytes,
false));
957 if (dst_h_ptr != src_h_ptr && bytes != 0)
959 MFEM_ASSERT((
const char*)dst_h_ptr + bytes <= src_h_ptr ||
960 (
const char*)src_h_ptr + bytes <= dst_h_ptr,
962 std::memcpy(dst_h_ptr, src_h_ptr, bytes);
967 if (dst_h_ptr != src_d_ptr && bytes != 0)
969 internal::Memory &dst_h_base = maps->memories.at(dst_h_ptr);
970 internal::Memory &src_d_base = maps->memories.at(src_d_ptr);
973 ctrl->Host(dst_h_mt)->Unprotect(dst_h_ptr, bytes);
974 ctrl->Device(src_d_mt)->DtoH(dst_h_ptr, src_d_ptr, bytes);
981 mm.GetAliasDevicePtr(dst_h_ptr, bytes,
false) :
982 mm.GetDevicePtr(dst_h_ptr, bytes,
false);
985 const bool known =
mm.
IsKnown(dst_h_ptr);
987 MFEM_VERIFY(alias||known,
"");
989 maps->memories.at(dst_h_ptr).d_mt :
990 maps->aliases.at(dst_h_ptr).mem->d_mt;
991 ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
995 if (dest_d_ptr != src_d_ptr && bytes != 0)
997 const bool known =
mm.
IsKnown(dst_h_ptr);
999 MFEM_VERIFY(alias||known,
"");
1001 maps->memories.at(dst_h_ptr).d_mt :
1002 maps->aliases.at(dst_h_ptr).mem->d_mt;
1003 ctrl->Device(d_mt)->DtoD(dest_d_ptr, src_d_ptr, bytes);
1009 void MemoryManager::CopyToHost_(
void *dest_h_ptr,
const void *src_h_ptr,
1010 size_t bytes,
unsigned src_flags)
1015 if (dest_h_ptr != src_h_ptr && bytes != 0)
1017 MFEM_ASSERT((
char*)dest_h_ptr + bytes <= src_h_ptr ||
1018 (
const char*)src_h_ptr + bytes <= dest_h_ptr,
1020 std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1025 MFEM_ASSERT(IsKnown_(src_h_ptr),
"internal error");
1026 const void *src_d_ptr = (src_flags &
Mem::ALIAS) ?
1027 mm.GetAliasDevicePtr(src_h_ptr, bytes,
false) :
1028 mm.GetDevicePtr(src_h_ptr, bytes,
false);
1029 const internal::Memory &base = maps->memories.at(dest_h_ptr);
1031 ctrl->Device(d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
1035 void MemoryManager::CopyFromHost_(
void *dest_h_ptr,
const void *src_h_ptr,
1036 size_t bytes,
unsigned &dest_flags)
1041 if (dest_h_ptr != src_h_ptr && bytes != 0)
1043 MFEM_ASSERT((
char*)dest_h_ptr + bytes <= src_h_ptr ||
1044 (
const char*)src_h_ptr + bytes <= dest_h_ptr,
1046 std::memcpy(dest_h_ptr, src_h_ptr, bytes);
1051 void *dest_d_ptr = (dest_flags &
Mem::ALIAS) ?
1052 mm.GetAliasDevicePtr(dest_h_ptr, bytes,
false) :
1053 mm.GetDevicePtr(dest_h_ptr, bytes,
false);
1054 const internal::Memory &base = maps->memories.at(dest_h_ptr);
1056 ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
1058 dest_flags = dest_flags &
1062 bool MemoryManager::IsKnown_(
const void *h_ptr)
1064 return maps->memories.find(h_ptr) != maps->memories.end();
1067 bool MemoryManager::IsAlias_(
const void *h_ptr)
1069 return maps->aliases.find(h_ptr) != maps->aliases.end();
1072 void MemoryManager::Insert(
void *h_ptr,
size_t bytes,
1077 MFEM_VERIFY(bytes == 0,
"Trying to add NULL with size " << bytes);
1080 MFEM_VERIFY_TYPES(h_mt, d_mt);
1084 maps->memories.emplace(h_ptr, internal::Memory(h_ptr, bytes, h_mt, d_mt));
1086 if (res.second ==
false)
1088 auto &m = res.first->second;
1089 MFEM_VERIFY(m.bytes >= bytes && m.h_mt == h_mt && m.d_mt == d_mt,
1090 "Address already present with different attributes!");
1095 void MemoryManager::InsertDevice(
void *d_ptr,
void *h_ptr,
size_t bytes,
1098 MFEM_VERIFY_TYPES(h_mt, d_mt);
1099 MFEM_ASSERT(h_ptr != NULL,
"internal error");
1100 Insert(h_ptr, bytes, h_mt, d_mt);
1101 internal::Memory &mem = maps->memories.at(h_ptr);
1102 if (d_ptr == NULL) { ctrl->Device(d_mt)->Alloc(mem); }
1103 else { mem.d_ptr = d_ptr; }
1106 void MemoryManager::InsertAlias(
const void *base_ptr,
void *alias_ptr,
1107 const size_t bytes,
const bool base_is_alias)
1109 size_t offset =
static_cast<size_t>(
static_cast<const char*
>(alias_ptr) -
1110 static_cast<const char*>(base_ptr));
1113 MFEM_VERIFY(offset == 0,
1114 "Trying to add alias to NULL at offset " << offset);
1119 const internal::Alias &alias = maps->aliases.at(base_ptr);
1120 MFEM_ASSERT(alias.mem,
"");
1121 base_ptr = alias.mem->h_ptr;
1122 offset += alias.offset;
1124 internal::Memory &mem = maps->memories.at(base_ptr);
1126 maps->aliases.emplace(alias_ptr,
1127 internal::Alias{&mem, offset, bytes, 1, mem.h_mt});
1128 if (res.second ==
false)
1130 if (res.first->second.mem != &mem || res.first->second.offset != offset)
1132 mfem_error(
"alias already exists with different base/offset!");
1136 res.first->second.counter++;
1141 void MemoryManager::Erase(
void *h_ptr,
bool free_dev_ptr)
1143 if (!h_ptr) {
return; }
1144 auto mem_map_iter = maps->memories.find(h_ptr);
1145 if (mem_map_iter == maps->memories.end()) {
mfem_error(
"Unknown pointer!"); }
1146 internal::Memory &mem = mem_map_iter->second;
1147 if (mem.d_ptr && free_dev_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem);}
1148 maps->memories.erase(mem_map_iter);
1151 void MemoryManager::EraseAlias(
void *alias_ptr)
1153 if (!alias_ptr) {
return; }
1154 auto alias_map_iter = maps->aliases.find(alias_ptr);
1155 if (alias_map_iter == maps->aliases.end()) {
mfem_error(
"Unknown alias!"); }
1156 internal::Alias &alias = alias_map_iter->second;
1157 if (--alias.counter) {
return; }
1158 maps->aliases.erase(alias_map_iter);
1161 void *MemoryManager::GetDevicePtr(
const void *h_ptr,
size_t bytes,
1166 MFEM_VERIFY(bytes == 0,
"Trying to access NULL with size " << bytes);
1169 internal::Memory &mem = maps->memories.at(h_ptr);
1172 MFEM_VERIFY_TYPES(h_mt, d_mt);
1173 if (!mem.d_ptr) { ctrl->Device(d_mt)->Alloc(mem); }
1174 ctrl->Device(d_mt)->Unprotect(mem);
1177 MFEM_ASSERT(bytes <= mem.bytes,
"invalid copy size");
1178 ctrl->Device(d_mt)->HtoD(mem.d_ptr, h_ptr, bytes);
1180 ctrl->Host(h_mt)->Protect(h_ptr, bytes);
1184 void *MemoryManager::GetAliasDevicePtr(
const void *alias_ptr,
size_t bytes,
1189 MFEM_VERIFY(bytes == 0,
"Trying to access NULL with size " << bytes);
1192 auto &alias_map = maps->aliases;
1193 auto alias_map_iter = alias_map.find(alias_ptr);
1194 if (alias_map_iter == alias_map.end()) {
mfem_error(
"alias not found"); }
1195 const internal::Alias &alias = alias_map_iter->second;
1196 const size_t offset = alias.offset;
1197 internal::Memory &mem = *alias.mem;
1200 MFEM_VERIFY_TYPES(h_mt, d_mt);
1201 if (!mem.d_ptr) { ctrl->Device(d_mt)->Alloc(mem); }
1202 void *alias_h_ptr =
static_cast<char*
>(mem.h_ptr) + offset;
1203 void *alias_d_ptr =
static_cast<char*
>(mem.d_ptr) + offset;
1204 MFEM_ASSERT(alias_h_ptr == alias_ptr,
"internal error");
1205 MFEM_ASSERT(bytes <= alias.bytes,
"internal error");
1206 ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes);
1207 ctrl->Host(h_mt)->AliasUnprotect(alias_ptr, bytes);
1208 if (copy) { ctrl->Device(d_mt)->HtoD(alias_d_ptr, alias_h_ptr, bytes); }
1209 ctrl->Host(h_mt)->AliasProtect(alias_ptr, bytes);
1213 void *MemoryManager::GetHostPtr(
const void *ptr,
size_t bytes,
bool copy)
1215 const internal::Memory &mem = maps->memories.at(ptr);
1216 MFEM_ASSERT(mem.h_ptr == ptr,
"internal error");
1217 MFEM_ASSERT(bytes <= mem.bytes,
"internal error")
1220 MFEM_VERIFY_TYPES(h_mt, d_mt);
1221 ctrl->Host(h_mt)->Unprotect(mem.h_ptr, bytes);
1223 if (mem.d_ptr) { ctrl->Device(d_mt)->Unprotect(mem); }
1224 if (copy && mem.d_ptr) { ctrl->Device(d_mt)->DtoH(mem.h_ptr, mem.d_ptr, bytes); }
1225 if (mem.d_ptr) { ctrl->Device(d_mt)->Protect(mem); }
1229 void *MemoryManager::GetAliasHostPtr(
const void *ptr,
size_t bytes,
1232 const internal::Alias &alias = maps->aliases.at(ptr);
1233 const internal::Memory *
const mem = alias.mem;
1236 MFEM_VERIFY_TYPES(h_mt, d_mt);
1237 void *alias_h_ptr =
static_cast<char*
>(mem->h_ptr) + alias.offset;
1238 void *alias_d_ptr = static_cast<char*>(mem->d_ptr) + alias.offset;
1239 MFEM_ASSERT(alias_h_ptr == ptr,
"internal error");
1240 ctrl->Host(h_mt)->AliasUnprotect(alias_h_ptr, bytes);
1241 if (mem->d_ptr) { ctrl->Device(d_mt)->AliasUnprotect(alias_d_ptr, bytes); }
1242 if (copy_data && mem->d_ptr)
1243 { ctrl->Device(d_mt)->DtoH(const_cast<void*>(ptr), alias_d_ptr, bytes); }
1244 if (mem->d_ptr) { ctrl->Device(d_mt)->AliasProtect(alias_d_ptr, bytes); }
1250 if (exists) {
return; }
1251 maps =
new internal::Maps();
1252 ctrl =
new internal::Ctrl();
1265 host_mem_type = host_mt;
1266 device_mem_type = device_mt;
1269 #ifdef MFEM_USE_UMPIRE
1273 h_umpire_name = h_name;
1274 d_umpire_name = d_name;
1280 MFEM_VERIFY(exists,
"MemoryManager has already been destroyed!");
1281 for (
auto& n : maps->memories)
1283 internal::Memory &mem = n.second;
1285 if (mem_h_ptr) { ctrl->Host(mem.h_mt)->Dealloc(mem.h_ptr); }
1286 if (mem.d_ptr) { ctrl->Device(mem.d_mt)->Dealloc(mem); }
1288 delete maps; maps =
nullptr;
1289 delete ctrl; ctrl =
nullptr;
1309 for (
const auto& n : maps->memories)
1311 const internal::Memory &mem = n.second;
1312 out <<
"\nkey " << n.first <<
", "
1313 <<
"h_ptr " << mem.h_ptr <<
", "
1314 <<
"d_ptr " << mem.d_ptr;
1317 if (maps->memories.size() > 0) { out << std::endl; }
1324 for (
const auto& n : maps->aliases)
1326 const internal::Alias &alias = n.second;
1327 out <<
"\nalias: key " << n.first <<
", "
1328 <<
"h_ptr " << alias.mem->h_ptr <<
", "
1329 <<
"offset " << alias.offset <<
", "
1330 <<
"bytes " << alias.bytes <<
", "
1331 <<
"counter " << alias.counter;
1334 if (maps->aliases.size() > 0) { out << std::endl; }
1338 int MemoryManager::CompareHostAndDevice_(
void *h_ptr,
size_t size,
1342 mm.GetAliasDevicePtr(h_ptr, size,
false) :
1343 mm.GetDevicePtr(h_ptr, size,
false);
1344 char *h_buf =
new char[size];
1346 int res = std::memcmp(h_ptr, h_buf, size);
1356 <<
"\n registered = " << bool(flags & Mem::REGISTERED)
1357 <<
"\n owns host = " << bool(flags & Mem::OWNS_HOST)
1358 <<
"\n owns device = " << bool(flags & Mem::OWNS_DEVICE)
1359 <<
"\n owns internal = " << bool(flags & Mem::OWNS_INTERNAL)
1360 <<
"\n valid host = " << bool(flags & Mem::VALID_HOST)
1361 <<
"\n valid device = " << bool(flags & Mem::VALID_DEVICE)
1362 <<
"\n device flag = " << bool(flags & Mem::USE_DEVICE)
1363 <<
"\n alias = " << bool(flags & Mem::ALIAS)
1367 void MemoryManager::CheckHostMemoryType_(
MemoryType h_mt,
void *h_ptr)
1369 if (!
mm.exists) {
return;}
1372 if (known) { MFEM_VERIFY(h_mt == maps->memories.at(h_ptr).h_mt,
""); }
1373 if (alias) { MFEM_VERIFY(h_mt == maps->aliases.at(h_ptr).mem->h_mt,
""); }
1378 bool MemoryManager::exists =
false;
1380 #ifdef MFEM_USE_UMPIRE
1381 const char* MemoryManager::h_umpire_name =
"HOST";
1382 const char* MemoryManager::d_umpire_name =
"DEVICE";
1390 "host-std",
"host-32",
"host-64",
"host-debug",
"host-umpire",
1391 #if defined(MFEM_USE_CUDA)
1394 #elif defined(MFEM_USE_HIP)
1402 #if defined(MFEM_USE_CUDA)
1404 #elif defined(MFEM_USE_HIP)
void * CuMemcpyHtoD(void *dst, const void *src, size_t bytes)
Copies memory from Host to Device.
void * CuMemFree(void *dptr)
Frees device memory.
Host memory; aligned at 64 bytes.
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
Device memory; using CUDA or HIP *Malloc and *Free.
void PrintFlags() const
Print the internal flags.
Device memory; using Umpire.
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
static MemoryType GetHostMemoryType()
Host memory; allocated from a "host-debug" pool.
void Configure(const MemoryType h_mt, const MemoryType d_mt)
int PrintAliases(std::ostream &out=mfem::out)
int CompareHostAndDevice(int size) const
If both the host and the device data are valid, compare their contents.
bool IsAlias(const void *h_ptr)
Return true if the pointer is known by the memory manager as an alias.
void * CuMallocManaged(void **dptr, size_t bytes)
Allocates managed device memory.
Host memory; aligned at 32 bytes.
void SetUmpireAllocatorNames(const char *h_name, const char *d_name)
Set the host and device UMpire allocator names.
The memory manager class.
constexpr int DeviceMemoryType
constexpr int HostMemoryType
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
static MemoryType GetDeviceMemoryType()
void * HipMemFree(void *dptr)
Frees device memory.
Ownership flag for internal Memory data.
void Destroy()
Free all the device memories.
int PrintPtrs(std::ostream &out=mfem::out)
The host pointer will be deleted by Delete()
void * CuMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
void RegisterCheck(void *h_ptr)
Check if the host pointer has been registered in the memory manager.
constexpr int MemoryTypeSize
Static casts to 'int' and sizes of some useful memory types.
void * HipMemcpyDtoH(void *dst, const void *src, size_t bytes)
Copies memory from Device to Host.
void * HipMemAlloc(void **dptr, size_t bytes)
Allocates device memory.
void Init()
Initialize the memory manager.
MemoryType
Memory types supported by MFEM.
bool IsKnown(const void *h_ptr)
Return true if the pointer is known by the memory manager.
constexpr int HostMemoryTypeSize
bool IsDeviceMemory(MemoryType mt)
MemoryManager mm
The (single) global memory manager object.
Host memory; using new[] and delete[].
void * HipMemcpyHtoD(void *dst, const void *src, size_t bytes)
Copies memory from Host to Device.
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
void * HipMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
constexpr int DeviceMemoryTypeSize
Host memory; using Umpire.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
Return a suitable MemoryClass from a pair of MemoryClasses.
void * CuMemAlloc(void **dptr, size_t bytes)
Allocates device memory.
MemoryClass
Memory classes identify sets of memory types.
void * CuMemcpyDtoH(void *dst, const void *src, size_t bytes)
Copies memory from Device to Host.
void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger. See also Memory<T>::PrintFlags().