12 #include "../general/forall.hpp"
17 #include <unordered_map>
50 return std::max(mc1, mc2);
67 const std::size_t bytes;
70 Memory(
void*
const h,
const std::size_t size):
71 host(true), bytes(size), h_ptr(h), d_ptr(nullptr) {}
79 unsigned long counter;
82 typedef std::unordered_map<const void*, Memory> MemoryMap;
85 typedef std::unordered_map<const void*, Alias*> AliasMap;
95 static internal::Ledger *maps;
100 maps =
new internal::Ledger();
110 MFEM_VERIFY(exists,
"MemoryManager has been destroyed already!");
111 for (
auto& n : maps->memories)
113 internal::Memory &mem = n.second;
116 for (
auto& n : maps->aliases)
124 void* MemoryManager::Insert(
void *ptr,
const std::size_t bytes)
128 MFEM_VERIFY(bytes == 0,
"Trying to add NULL with size " << bytes);
131 auto res = maps->memories.emplace(ptr, internal::Memory(ptr, bytes));
132 if (res.second ==
false)
134 mfem_error(
"Trying to add an already present address!");
139 void MemoryManager::InsertDevice(
void *ptr,
void *h_ptr,
size_t bytes)
141 MFEM_VERIFY(ptr != NULL,
"cannot register NULL device pointer");
142 MFEM_VERIFY(h_ptr != NULL,
"internal error");
143 auto res = maps->memories.emplace(h_ptr, internal::Memory(h_ptr, bytes));
144 if (res.second ==
false)
146 mfem_error(
"Trying to add an already present address!");
148 res.first->second.d_ptr = ptr;
151 void *MemoryManager::Erase(
void *ptr,
bool free_dev_ptr)
153 if (!ptr) {
return ptr; }
154 auto mem_map_iter = maps->memories.find(ptr);
155 if (mem_map_iter == maps->memories.end())
157 mfem_error(
"Trying to erase an unknown pointer!");
159 internal::Memory &mem = mem_map_iter->second;
160 if (mem.d_ptr && free_dev_ptr) {
CuMemFree(mem.d_ptr); }
161 maps->memories.erase(mem_map_iter);
165 bool MemoryManager::IsKnown(
const void *ptr)
167 return maps->memories.find(ptr) != maps->memories.end();
170 void *MemoryManager::GetDevicePtr(
const void *ptr,
size_t bytes,
bool copy_data)
174 MFEM_VERIFY(bytes == 0,
"Trying to access NULL with size " << bytes);
177 internal::Memory &base = maps->memories.at(ptr);
184 MFEM_ASSERT(bytes <= base.bytes,
"invalid copy size");
191 void MemoryManager::InsertAlias(
const void *base_ptr,
void *alias_ptr,
194 long offset =
static_cast<const char*
>(alias_ptr) -
195 static_cast<const char*>(base_ptr);
198 MFEM_VERIFY(offset == 0,
199 "Trying to add alias to NULL at offset " << offset);
204 const internal::Alias *alias = maps->aliases.at(base_ptr);
205 base_ptr = alias->mem->h_ptr;
206 offset += alias->offset;
208 internal::Memory &mem = maps->memories.at(base_ptr);
209 auto res = maps->aliases.emplace(alias_ptr,
nullptr);
210 if (res.second ==
false)
212 if (res.first->second->mem != &mem || res.first->second->offset != offset)
214 mfem_error(
"alias already exists with different base/offset!");
218 res.first->second->counter++;
223 res.first->second =
new internal::Alias{&mem, offset, 1};
227 void MemoryManager::EraseAlias(
void *alias_ptr)
229 if (!alias_ptr) {
return; }
230 auto alias_map_iter = maps->aliases.find(alias_ptr);
231 if (alias_map_iter == maps->aliases.end())
235 internal::Alias *alias = alias_map_iter->second;
236 if (--alias->counter) {
return; }
238 maps->aliases.erase(alias_map_iter);
242 void *MemoryManager::GetAliasDevicePtr(
const void *alias_ptr,
size_t bytes,
247 MFEM_VERIFY(bytes == 0,
"Trying to access NULL with size " << bytes);
250 auto &alias_map = maps->aliases;
251 auto alias_map_iter = alias_map.find(alias_ptr);
252 if (alias_map_iter == alias_map.end())
256 const internal::Alias *alias = alias_map_iter->second;
257 internal::Memory &base = *alias->mem;
258 MFEM_ASSERT((
char*)base.h_ptr + alias->offset == alias_ptr,
266 CuMemcpyHtoD((
char*)base.d_ptr + alias->offset, alias_ptr, bytes);
269 return (
char*)base.d_ptr + alias->offset;
272 static void PullKnown(internal::Ledger *maps,
273 const void *ptr,
const std::size_t bytes,
bool copy_data)
275 internal::Memory &base = maps->memories.at(ptr);
276 MFEM_ASSERT(base.h_ptr == ptr,
"internal error");
280 if (copy_data && base.d_ptr)
287 static void PullAlias(
const internal::Ledger *maps,
288 const void *ptr,
const std::size_t bytes,
bool copy_data)
290 const internal::Alias *alias = maps->aliases.at(ptr);
291 MFEM_ASSERT((
char*)alias->mem->h_ptr + alias->offset == ptr,
296 if (copy_data && alias->mem->d_ptr)
299 static_cast<char*>(alias->mem->d_ptr) + alias->offset,
317 for (
const auto& n : maps->memories)
319 const internal::Memory &mem = n.second;
321 <<
"key " << n.first <<
", "
322 <<
"host " << mem.host <<
", "
323 <<
"h_ptr " << mem.h_ptr <<
", "
324 <<
"d_ptr " << mem.d_ptr;
331 void *MemoryManager::New_(
void *h_ptr, std::size_t size,
MemoryType mt,
342 mfem_error(
"New_(): aligned host types are not implemented yet");
346 mm.Insert(h_ptr, size);
351 mfem_error(
"New_(): CUDA UVM allocation is not implemented yet");
357 void *MemoryManager::Register_(
void *ptr,
void *h_ptr, std::size_t capacity,
362 MFEM_VERIFY(alias ==
false,
"cannot register an alias!");
366 mm.Insert(ptr, capacity);
367 flags = (own ? flags |
Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST) |
372 mm.InsertDevice(ptr, h_ptr, capacity);
378 void MemoryManager::Alias_(
void *base_h_ptr, std::size_t offset,
379 std::size_t size,
unsigned base_flags,
383 mm.InsertAlias(base_h_ptr, (
char*)base_h_ptr + offset,
389 MemoryType MemoryManager::Delete_(
void *h_ptr,
unsigned flags)
395 "invalid Memory state");
398 if (flags & Mem::ALIAS)
400 mm.EraseAlias(h_ptr);
404 mm.Erase(h_ptr, flags & Mem::OWNS_DEVICE);
410 void *MemoryManager::ReadWrite_(
void *h_ptr,
MemoryClass mc,
411 std::size_t size,
unsigned &flags)
418 if (flags & Mem::ALIAS) { PullAlias(maps, h_ptr, size,
true); }
419 else { PullKnown(maps, h_ptr, size,
true); }
442 if (flags & Mem::ALIAS)
444 return mm.GetAliasDevicePtr(h_ptr, size, need_copy);
446 return mm.GetDevicePtr(h_ptr, size, need_copy);
459 const void *MemoryManager::Read_(
void *h_ptr,
MemoryClass mc,
460 std::size_t size,
unsigned &flags)
465 if (!(flags & Mem::VALID_HOST))
467 if (flags & Mem::ALIAS) { PullAlias(maps, h_ptr, size,
true); }
468 else { PullKnown(maps, h_ptr, size,
true); }
491 if (flags & Mem::ALIAS)
493 return mm.GetAliasDevicePtr(h_ptr, size, need_copy);
495 return mm.GetDevicePtr(h_ptr, size, need_copy);
508 void *MemoryManager::Write_(
void *h_ptr,
MemoryClass mc, std::size_t size,
537 if (flags & Mem::ALIAS)
539 return mm.GetAliasDevicePtr(h_ptr, size,
false);
541 return mm.GetDevicePtr(h_ptr, size,
false);
553 void MemoryManager::SyncAlias_(
const void *base_h_ptr,
void *alias_h_ptr,
554 size_t alias_size,
unsigned base_flags,
555 unsigned &alias_flags)
559 MFEM_ASSERT(alias_flags & Mem::ALIAS,
"not an alias");
560 if ((base_flags & Mem::VALID_HOST) && !(alias_flags & Mem::VALID_HOST))
562 PullAlias(maps, alias_h_ptr, alias_size,
true);
568 mm.InsertAlias(base_h_ptr, alias_h_ptr, base_flags & Mem::ALIAS);
572 mm.GetAliasDevicePtr(alias_h_ptr, alias_size,
true);
578 MemoryType MemoryManager::GetMemoryType_(
void *h_ptr,
unsigned flags)
585 void MemoryManager::Copy_(
void *dest_h_ptr,
const void *src_h_ptr,
586 std::size_t size,
unsigned src_flags,
587 unsigned &dest_flags)
597 const bool src_on_host =
599 (!(src_flags & Mem::VALID_DEVICE) ||
600 ((dest_flags &
Mem::VALID_HOST) && !(dest_flags & Mem::VALID_DEVICE)));
601 const bool dest_on_host =
603 (!(dest_flags & Mem::VALID_DEVICE) ||
605 const void *src_d_ptr = src_on_host ? NULL :
607 mm.GetAliasDevicePtr(src_h_ptr, size,
false) :
608 mm.GetDevicePtr(src_h_ptr, size,
false));
613 if (dest_h_ptr != src_h_ptr && size != 0)
615 MFEM_ASSERT((
char*)dest_h_ptr + size <= src_h_ptr ||
616 (
char*)src_h_ptr + size <= dest_h_ptr,
618 std::memcpy(dest_h_ptr, src_h_ptr, size);
628 void *dest_d_ptr = (dest_flags &
Mem::ALIAS) ?
629 mm.GetAliasDevicePtr(dest_h_ptr, size,
false) :
630 mm.GetDevicePtr(dest_h_ptr, size,
false);
640 dest_flags = dest_flags &
644 void MemoryManager::CopyToHost_(
void *dest_h_ptr,
const void *src_h_ptr,
645 std::size_t size,
unsigned src_flags)
650 if (dest_h_ptr != src_h_ptr && size != 0)
652 MFEM_ASSERT((
char*)dest_h_ptr + size <= src_h_ptr ||
653 (
char*)src_h_ptr + size <= dest_h_ptr,
655 std::memcpy(dest_h_ptr, src_h_ptr, size);
660 const void *src_d_ptr = (src_flags &
Mem::ALIAS) ?
661 mm.GetAliasDevicePtr(src_h_ptr, size,
false) :
662 mm.GetDevicePtr(src_h_ptr, size,
false);
667 void MemoryManager::CopyFromHost_(
void *dest_h_ptr,
const void *src_h_ptr,
668 std::size_t size,
unsigned &dest_flags)
673 if (dest_h_ptr != src_h_ptr && size != 0)
675 MFEM_ASSERT((
char*)dest_h_ptr + size <= src_h_ptr ||
676 (
char*)src_h_ptr + size <= dest_h_ptr,
678 std::memcpy(dest_h_ptr, src_h_ptr, size);
683 void *dest_d_ptr = (dest_flags &
Mem::ALIAS) ?
684 mm.GetAliasDevicePtr(dest_h_ptr, size,
false) :
685 mm.GetDevicePtr(dest_h_ptr, size,
false);
688 dest_flags = dest_flags &
697 <<
" registered = " << bool(flags & Mem::REGISTERED)
698 <<
"\n owns host = " << bool(flags & Mem::OWNS_HOST)
699 <<
"\n owns device = " << bool(flags & Mem::OWNS_DEVICE)
700 <<
"\n owns internal = " << bool(flags & Mem::OWNS_INTERNAL)
701 <<
"\n valid host = " << bool(flags & Mem::VALID_HOST)
702 <<
"\n valid device = " << bool(flags & Mem::VALID_DEVICE)
703 <<
"\n alias = " << bool(flags & Mem::ALIAS)
704 <<
"\n device flag = " << bool(flags & Mem::USE_DEVICE)
710 bool MemoryManager::exists =
false;
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 (not supported yet)
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
The device pointer will be deleted by Delete()
Memory types: { CUDA, CUDA_UVM }.
Host memory aligned at 32 bytes (not supported yet)
The memory manager class.
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
void * CuMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
h_ptr is registered with the MemoryManager
The host pointer will be deleted by Delete()
Ownership flag for internal Memory data.
MemoryType
Memory types supported by MFEM.
void RegisterCheck(void *ptr)
Check if pointer has been registered in the memory manager.
MemoryManager mm
The (single) global memory manager object.
Host memory; using new[] and delete[].
void PrintPtrs(void)
Prints all pointers known by the memory manager.
cudaMallocManaged, cudaFree (not supported yet)
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
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 subsets 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.