23#include <unordered_map>
38occa::device occaDevice;
60 "ceed-cuda",
"occa-cuda",
"raja-cuda",
"cuda",
61 "ceed-hip",
"raja-hip",
"hip",
"debug",
62 "occa-omp",
"raja-omp",
"omp",
63 "ceed-cpu",
"occa-cpu",
"raja-cpu",
"cpu"
70Device Device::device_singleton;
71bool Device::device_env =
false;
72bool Device::mem_host_env =
false;
73bool Device::mem_device_env =
false;
74bool Device::mem_types_set =
false;
78 if (
GetEnv(
"MFEM_MEMORY") && !mem_host_env && !mem_device_env)
80 std::string mem_backend(
GetEnv(
"MFEM_MEMORY"));
81 if (mem_backend ==
"host")
87 else if (mem_backend ==
"host32")
93 else if (mem_backend ==
"host64")
99 else if (mem_backend ==
"umpire")
108 else if (mem_backend ==
"debug")
119 || mem_backend ==
"cuda"
122 || mem_backend ==
"hip"
128 mem_device_env =
true;
131 else if (mem_backend ==
"uvm")
134 mem_device_env =
true;
140 MFEM_ABORT(
"Unknown memory backend!");
145 if (
GetEnv(
"MFEM_DEVICE"))
147 std::string device(
GetEnv(
"MFEM_DEVICE"));
152 if (
GetEnv(
"MFEM_GPU_AWARE_MPI"))
163 if ( device_env && !destroy_mm) {
return; }
164 if (!device_env && destroy_mm && !mem_host_env)
168 for (
auto entry : internal::ceed_basis_map)
170 CeedBasisDestroy(&entry.second);
172 internal::ceed_basis_map.clear();
173 for (
auto entry : internal::ceed_restr_map)
175 CeedElemRestrictionDestroy(&entry.second);
177 internal::ceed_restr_map.clear();
179 CeedDestroy(&internal::ceed);
197 std::memcpy((
void*)
this, &Get(),
sizeof(
Device));
198 Get().destroy_mm =
false;
202 std::map<std::string, Backend::Id> bmap;
205 bmap[internal::backend_name[i]] = internal::backend_list[i];
218#elif defined(MFEM_USE_CUDA)
230 std::string device_option;
231 std::string::size_type beg = 0, end;
234 end = device.find(
',', beg);
235 end = (end != std::string::npos) ? end : device.size();
236 const std::string bname = device.substr(beg, end - beg);
237 const auto option = bname.find(
':');
238 const std::string backend = (option != std::string::npos) ?
239 bname.substr(0, option) : bname;
240 const auto it = bmap.find(backend);
241 MFEM_VERIFY(it != bmap.end(),
"Invalid backend name: '" << backend <<
'\'');
242 Get().MarkBackend(it->second);
243 if (option != std::string::npos)
245 device_option += bname.substr(option);
247 if (end == device.size()) {
break; }
263#ifdef MFEM_USE_OPENMP
271 Get().Setup(device_option, device_id);
274 Get().UpdateMemoryTypeAndClass(device_option);
277 if (
this != &Get()) { std::memcpy((
void*)
this, &Get(),
sizeof(
Device)); }
283#if defined(HYPRE_USING_GPU) && (MFEM_HYPRE_VERSION >= 23100)
287 if (HYPRE_Initialized())
301 if (mem_host_env || mem_device_env || device_env) {
return; }
303 MFEM_VERIFY(!
IsConfigured(),
"the default MemoryTypes can only be set before"
304 " Device construction and configuration");
306 "invalid host MemoryType, h_mt = " << (
int)h_mt);
308 "invalid device MemoryType, d_mt = " << (
int)d_mt
309 <<
" (h_mt = " << (
int)h_mt <<
')');
311 Get().host_mem_type = h_mt;
312 Get().device_mem_type = d_mt;
313 mem_types_set =
true;
321 os <<
"Device configuration: ";
322 bool add_comma =
false;
325 if (backends & internal::backend_list[i])
327 if (add_comma) { os <<
','; }
329 os << internal::backend_name[i];
336 const char *ceed_backend;
337 CeedGetResource(internal::ceed, &ceed_backend);
338 os <<
"libCEED backend: " << ceed_backend <<
'\n';
341 os <<
"Memory configuration: "
345 os << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
351 os <<
"\nUse GPU-aware MPI: " << (
GetGPUAwareMPI() ?
"yes" :
"no");
357void Device::UpdateMemoryTypeAndClass(
const std::string &device_option)
362#ifdef MFEM_USE_UMPIRE
364 if (!mem_host_env && !mem_types_set)
381 switch (host_mem_type)
393 else if (!mem_types_set)
395#ifndef MFEM_USE_UMPIRE
406 if (device && device_option.find(
":uvm") != std::string::npos)
420 "invalid device memory configuration!");
429 if (Get().ngpu >= 0) {
return Get().ngpu; }
430#if defined(MFEM_USE_CUDA)
432#elif defined(MFEM_USE_HIP)
434 MFEM_GPU_CHECK(hipGetDeviceCount(&ngpu));
437 MFEM_ABORT(
"Unable to query number of available devices without"
438 " MFEM_USE_CUDA or MFEM_USE_HIP!");
443static void CudaDeviceSetup(
const int dev,
int &ngpu)
447 MFEM_VERIFY(ngpu > 0,
"No CUDA device found!");
448 MFEM_GPU_CHECK(cudaSetDevice(dev));
450 MFEM_CONTRACT_VAR(dev);
451 MFEM_CONTRACT_VAR(ngpu);
455static void HipDeviceSetup(
const int dev,
int &ngpu)
458 MFEM_GPU_CHECK(hipGetDeviceCount(&ngpu));
459 MFEM_VERIFY(ngpu > 0,
"No HIP device found!");
460 MFEM_GPU_CHECK(hipSetDevice(dev));
462 MFEM_CONTRACT_VAR(dev);
463 MFEM_CONTRACT_VAR(ngpu);
467static void RajaDeviceSetup(
const int dev,
int &ngpu)
470 CudaDeviceSetup(dev, ngpu);
471#elif defined(MFEM_USE_HIP)
472 HipDeviceSetup(dev, ngpu);
474 MFEM_CONTRACT_VAR(dev);
475 MFEM_CONTRACT_VAR(ngpu);
479static void OccaDeviceSetup(
const int dev)
485 if (cpu + omp + cuda > 1)
487 MFEM_ABORT(
"Only one OCCA backend can be configured at a time!");
492 std::string mode(
"mode: 'CUDA', device_id : ");
493 internal::occaDevice.setup(mode.append(1,
'0'+dev));
495 MFEM_ABORT(
"the OCCA CUDA backend requires OCCA built with CUDA!");
500#if OCCA_OPENMP_ENABLED
501 internal::occaDevice.setup(
"mode: 'OpenMP'");
503 MFEM_ABORT(
"the OCCA OpenMP backend requires OCCA built with OpenMP!");
508 internal::occaDevice.setup(
"mode: 'Serial'");
512 if (occa::io::exists(MFEM_INSTALL_DIR
"/include/mfem/"))
514 mfemDir = MFEM_INSTALL_DIR
"/include/mfem/";
516 else if (occa::io::exists(MFEM_SOURCE_DIR))
518 mfemDir = MFEM_SOURCE_DIR;
522 MFEM_ABORT(
"Cannot find OCCA kernels in MFEM_INSTALL_DIR or MFEM_SOURCE_DIR");
525 occa::io::addLibraryPath(
"mfem", mfemDir);
526 occa::loadKernels(
"mfem");
528 MFEM_CONTRACT_VAR(dev);
529 MFEM_ABORT(
"the OCCA backends require MFEM built with MFEM_USE_OCCA=YES");
533static void CeedDeviceSetup(
const char* ceed_spec)
536 CeedInit(ceed_spec, &internal::ceed);
537 const char *ceed_backend;
538 CeedGetResource(internal::ceed, &ceed_backend);
539 if (strcmp(ceed_spec, ceed_backend) && strcmp(ceed_spec,
"/cpu/self") &&
540 strcmp(ceed_spec,
"/gpu/hip"))
543 "libCEED is not using the requested backend!!!\n"
544 "WARNING!!!\n" << std::endl;
547 CeedSetErrorHandler(internal::ceed, CeedErrorStore);
550 MFEM_CONTRACT_VAR(ceed_spec);
554void Device::Setup(
const std::string &device_option,
const int device_id)
556 MFEM_VERIFY(ngpu == -1,
"the mfem::Device is already configured!");
562 "the CUDA backends require MFEM built with MFEM_USE_CUDA=YES");
566 "the HIP backends require MFEM built with MFEM_USE_HIP=YES");
570 "the RAJA backends require MFEM built with MFEM_USE_RAJA=YES");
572#ifndef MFEM_USE_OPENMP
574 "the OpenMP and RAJA OpenMP backends require MFEM built with"
575 " MFEM_USE_OPENMP=YES");
579 "the CEED backends require MFEM built with MFEM_USE_CEED=YES");
585 RajaDeviceSetup(dev, ngpu);
594 MFEM_VERIFY(ceed_cpu + ceed_cuda + ceed_hip == 1,
595 "Only one CEED backend can be enabled at a time!");
599 const char *ceed_spec_search =
603 const char *ceed_spec_default =
607 std::string::size_type beg = device_option.find(ceed_spec_search), end;
608 if (beg == std::string::npos)
610 CeedDeviceSetup(ceed_spec_default);
614 end = device_option.find(
':', beg + 1);
615 end = (end != std::string::npos) ? end : device_option.size();
616 CeedDeviceSetup(device_option.substr(beg + 1, end - beg - 1).c_str());
626#if defined(MFEM_USE_CUDA)
627 struct cudaPointerAttributes attr;
629#if (CUDART_VERSION >= 11000)
630 MFEM_GPU_CHECK(cudaPointerGetAttributes(&attr, ptr));
632 cudaPointerGetAttributes(&attr, ptr);
633 if (
err != cudaSuccess)
641 case cudaMemoryTypeUnregistered:
644 case cudaMemoryTypeHost:
647 case cudaMemoryTypeDevice:
650 case cudaMemoryTypeManaged:
655#elif defined(MFEM_USE_HIP)
656 struct hipPointerAttribute_t attr;
658 hipError_t error = hipPointerGetAttributes(&attr, ptr);
659 if (error != hipSuccess)
661 if (error == hipErrorInvalidValue)
665 (void)hipGetLastError();
669 MFEM_GPU_CHECK(error);
672 else if (attr.isManaged)
676#if (HIP_VERSION_MAJOR >= 6)
677 else if (attr.type == hipMemoryTypeDevice)
679 else if (attr.memoryType == hipMemoryTypeDevice)
684#if (HIP_VERSION_MAJOR >= 6)
685 else if (attr.type == hipMemoryTypeHost)
687 else if (attr.memoryType == hipMemoryTypeHost)
692#if (HIP_VERSION_MAJOR >= 6)
693 else if (attr.type == hipMemoryTypeUnregistered)
699 MFEM_CONTRACT_VAR(ptr);
706#if defined(MFEM_USE_CUDA)
707 cudaMemGetInfo(free, total);
708#elif defined(MFEM_USE_HIP)
709 MFEM_GPU_CHECK(hipMemGetInfo(free, total));
725 std::stringstream res;
726#if defined(MFEM_USE_CUDA)
728 MFEM_GPU_CHECK(cudaGetDeviceProperties(&prop, device_id));
729 for (
int i = 0; i < 16; ++i)
731 const unsigned b =
static_cast<unsigned char>(prop.uuid.bytes[i]);
732 res << std::setfill(
'0') << std::setw(2) << std::hex <<
b;
734#elif defined(MFEM_USE_HIP)
736 MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
737 for (
int i = 0; i < 16; ++i)
739 const unsigned b =
static_cast<unsigned char>(uuid.bytes[i]);
740 res << std::setfill(
'0') << std::setw(2) << std::hex <<
b;
748#if defined(MFEM_USE_CUDA)
750 cudaDeviceGetAttribute(&res, cudaDevAttrMultiProcessorCount, dev);
752#elif defined(MFEM_USE_HIP)
755 hipDeviceGetAttribute(&res, hipDeviceAttributeMultiprocessorCount, dev));
759 MFEM_CONTRACT_VAR(dev);
767#if defined(MFEM_USE_CUDA)
769#elif defined(MFEM_USE_HIP)
770 MFEM_GPU_CHECK(hipGetDevice(&dev));
777#if defined(MFEM_USE_CUDA)
779 cudaDeviceGetAttribute(&res, cudaDevAttrWarpSize, dev);
781#elif defined(MFEM_USE_HIP)
783 MFEM_GPU_CHECK(hipDeviceGetAttribute(&res, hipDeviceAttributeWarpSize, dev));
787 MFEM_CONTRACT_VAR(dev);
795#if defined(MFEM_USE_CUDA)
797#elif defined(MFEM_USE_HIP)
798 MFEM_GPU_CHECK(hipGetDevice(&dev));
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
static void DeviceMem(size_t *free, size_t *total)
Gets the free and total memory on the device.
void Configure(const std::string &device, const int device_id=0)
Configure the Device backends.
static void SetGPUAwareMPI(const bool force=true)
Manually set the status of GPU-aware MPI flag for use in MPI communication routines which have optimi...
static int NumMultiprocessors()
Same as NumMultiprocessors(int), for the currently active device.
static bool IsConfigured()
Return true if Configure() has been called previously.
static MemoryType QueryMemoryType(const void *ptr)
static std::string GetUUID(const int device_id=0)
void Print(std::ostream &os=mfem::out)
Print the configuration of the MFEM virtual device object.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
static void SetMemoryTypes(MemoryType h_mt, MemoryType d_mt)
Set the default host and device MemoryTypes, h_mt and d_mt.
static bool GetGPUAwareMPI()
Get the status of GPU-aware MPI flag.
static int WarpSize()
Same as WarpSize(int), for the currently active device.
static int GetDeviceCount()
Get the number of available devices (may be called before configuration).
Device()
Default constructor. Unless Configure() is called later, the default Backend::CPU will be used.
static void InitDevice()
Configure HYPRE's compute and memory policy.
static void Finalize()
Finalize hypre (called automatically at program exit if Hypre::Init() has been called).
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...
void Destroy()
Free all the device memories.
static bool IsFinalized()
Return true if MPI has been finalized.
static bool IsInitialized()
Return true if MPI has been initialized.
std::unordered_map< const BasisKey, CeedBasis, BasisHash > BasisMap
std::unordered_map< const RestrKey, CeedElemRestriction, RestrHash > RestrMap
MFEM_HOST_DEVICE tensor< T, n, n > dev(const tensor< T, n, n > &A)
Calculates the deviator of a matrix (rank-2 tensor)
bool IsDeviceMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::DEVICE.
const char * GetEnv(const char *name)
Wrapper for std::getenv.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
MemoryManager mm
The (single) global memory manager object.
int CuGetDeviceCount()
Get the number of CUDA devices.
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.
OutStream err(std::cerr)
Global stream used by the library for standard error output. Initially it uses the same std::streambu...
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.
Id
In the documentation below, we use square brackets to indicate the type of the backend: host or devic...
@ RAJA_OMP
[host] RAJA OpenMP backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_OPENMP = YES.
@ RAJA_CUDA
[device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_CUDA = YES.
@ DEBUG_DEVICE
[device] Debug backend: host memory is READ/WRITE protected while a device is in use....
@ RAJA_CPU
[host] RAJA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_RAJA = YES.
@ OMP
[host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
@ HIP
[device] HIP backend. Enabled when MFEM_USE_HIP = YES.
@ OCCA_OMP
[host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
@ RAJA_HIP
[device] RAJA HIP backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_HIP = YES.
@ OCCA_CUDA
[device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES and MFEM_USE_CUDA = YES.
@ CEED_CPU
[host] CEED CPU backend. GPU backends can still be used, but with expensive memory transfers....
@ OCCA_CPU
[host] OCCA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_OCCA = YES.
@ CEED_CUDA
[device] CEED CUDA backend working together with the CUDA backend. Enabled when MFEM_USE_CEED = YES a...
@ CPU
[host] Default CPU backend: sequential execution on each MPI rank.
@ CUDA
[device] CUDA backend. Enabled when MFEM_USE_CUDA = YES.
@ CEED_HIP
[device] CEED HIP backend working together with the HIP backend. Enabled when MFEM_USE_CEED = YES and...
@ RAJA_MASK
Biwise-OR of all RAJA backends.
@ DEVICE_MASK
Biwise-OR of all device backends.
@ CEED_MASK
Bitwise-OR of all CEED backends.
@ OCCA_MASK
Biwise-OR of all OCCA backends.
@ HIP_MASK
Biwise-OR of all HIP backends.
@ NUM_BACKENDS
Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
@ CUDA_MASK
Biwise-OR of all CUDA backends.