15 #include "../fem/ceed/interface/util.hpp"
18 #include <unordered_map>
32 occa::device occaDevice;
54 "ceed-cuda",
"occa-cuda",
"raja-cuda",
"cuda",
55 "ceed-hip",
"raja-hip",
"hip",
"debug",
56 "occa-omp",
"raja-omp",
"omp",
57 "ceed-cpu",
"occa-cpu",
"raja-cpu",
"cpu"
64 Device Device::device_singleton;
65 bool Device::device_env =
false;
66 bool Device::mem_host_env =
false;
67 bool Device::mem_device_env =
false;
68 bool Device::mem_types_set =
false;
72 if (getenv(
"MFEM_MEMORY") && !mem_host_env && !mem_device_env)
74 std::string mem_backend(getenv(
"MFEM_MEMORY"));
75 if (mem_backend ==
"host")
81 else if (mem_backend ==
"host32")
87 else if (mem_backend ==
"host64")
93 else if (mem_backend ==
"umpire")
102 else if (mem_backend ==
"debug")
113 || mem_backend ==
"cuda"
116 || mem_backend ==
"hip"
122 mem_device_env =
true;
125 else if (mem_backend ==
"uvm")
128 mem_device_env =
true;
134 MFEM_ABORT(
"Unknown memory backend!");
139 if (getenv(
"MFEM_DEVICE"))
141 std::string device(getenv(
"MFEM_DEVICE"));
150 if ( device_env && !destroy_mm) {
return; }
151 if (!device_env && destroy_mm && !mem_host_env)
156 for (
auto entry : internal::ceed_basis_map)
158 CeedBasisDestroy(&entry.second);
160 internal::ceed_basis_map.clear();
161 for (
auto entry : internal::ceed_restr_map)
163 CeedElemRestrictionDestroy(&entry.second);
165 internal::ceed_restr_map.clear();
167 CeedDestroy(&internal::ceed);
172 Get().mode = SEQUENTIAL;
186 std::memcpy(
this, &Get(),
sizeof(
Device));
187 Get().destroy_mm =
false;
191 std::map<std::string, Backend::Id> bmap;
194 bmap[internal::backend_name[i]] = internal::backend_list[i];
196 std::string::size_type beg = 0, end, option;
199 end = device.find(
',', beg);
200 end = (end != std::string::npos) ? end : device.size();
201 const std::string bname = device.substr(beg, end - beg);
202 option = bname.find(
':');
203 if (option==std::string::npos)
205 const std::string backend = bname;
206 std::map<std::string, Backend::Id>::iterator it = bmap.find(backend);
207 MFEM_VERIFY(it != bmap.end(),
"invalid backend name: '" << backend <<
'\'');
208 Get().MarkBackend(it->second);
212 const std::string backend = bname.substr(0, option);
213 const std::string boption = bname.substr(option+1);
214 Get().device_option = strdup(boption.c_str());
215 std::map<std::string, Backend::Id>::iterator it = bmap.find(backend);
216 MFEM_VERIFY(it != bmap.end(),
"invalid backend name: '" << backend <<
'\'');
217 Get().MarkBackend(it->second);
219 if (end == device.size()) {
break; }
235 #ifdef MFEM_USE_OPENMP
243 Get().Setup(device_id);
249 if (
this != &Get()) { std::memcpy(
this, &Get(),
sizeof(
Device)); }
261 if (mem_host_env || mem_device_env || device_env) {
return; }
263 MFEM_VERIFY(!
IsConfigured(),
"the default MemoryTypes can only be set before"
264 " Device construction and configuration");
266 "invalid host MemoryType, h_mt = " << (
int)h_mt);
268 "invalid device MemoryType, d_mt = " << (
int)d_mt
269 <<
" (h_mt = " << (
int)h_mt <<
')');
271 Get().host_mem_type = h_mt;
272 Get().device_mem_type = d_mt;
273 mem_types_set =
true;
281 os <<
"Device configuration: ";
282 bool add_comma =
false;
285 if (backends & internal::backend_list[i])
287 if (add_comma) { os <<
','; }
289 os << internal::backend_name[i];
296 const char *ceed_backend;
297 CeedGetResource(internal::ceed, &ceed_backend);
298 os <<
"libCEED backend: " << ceed_backend <<
'\n';
301 os <<
"Memory configuration: "
305 os << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
310 void Device::UpdateMemoryTypeAndClass()
316 #ifdef MFEM_USE_UMPIRE
318 if (!mem_host_env && !mem_types_set)
335 switch (host_mem_type)
347 else if (!mem_types_set)
349 #ifndef MFEM_USE_UMPIRE
360 if (device && device_option && !strcmp(device_option,
"uvm"))
374 "invalid device memory configuration!");
380 void Device::Enable()
382 const bool accelerated = Get().backends & ~(
Backend::CPU);
383 if (accelerated) { Get().mode = Device::ACCELERATED;}
384 Get().UpdateMemoryTypeAndClass();
388 static void DeviceSetup(
const int dev,
int &ngpu)
391 MFEM_VERIFY(ngpu > 0,
"No CUDA device found!");
392 MFEM_GPU_CHECK(cudaSetDevice(dev));
396 static void CudaDeviceSetup(
const int dev,
int &ngpu)
399 DeviceSetup(dev, ngpu);
401 MFEM_CONTRACT_VAR(dev);
402 MFEM_CONTRACT_VAR(ngpu);
406 static void HipDeviceSetup(
const int dev,
int &ngpu)
409 MFEM_GPU_CHECK(hipGetDeviceCount(&ngpu));
410 MFEM_VERIFY(ngpu > 0,
"No HIP device found!");
411 MFEM_GPU_CHECK(hipSetDevice(dev));
413 MFEM_CONTRACT_VAR(dev);
414 MFEM_CONTRACT_VAR(ngpu);
418 static void RajaDeviceSetup(
const int dev,
int &ngpu)
421 if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
422 #elif defined(MFEM_USE_HIP)
423 HipDeviceSetup(dev, ngpu);
425 MFEM_CONTRACT_VAR(dev);
426 MFEM_CONTRACT_VAR(ngpu);
430 static void OccaDeviceSetup(
const int dev)
436 if (cpu + omp + cuda > 1)
438 MFEM_ABORT(
"Only one OCCA backend can be configured at a time!");
442 #if OCCA_CUDA_ENABLED
443 std::string mode(
"mode: 'CUDA', device_id : ");
444 internal::occaDevice.setup(mode.append(1,
'0'+dev));
446 MFEM_ABORT(
"the OCCA CUDA backend requires OCCA built with CUDA!");
451 #if OCCA_OPENMP_ENABLED
452 internal::occaDevice.setup(
"mode: 'OpenMP'");
454 MFEM_ABORT(
"the OCCA OpenMP backend requires OCCA built with OpenMP!");
459 internal::occaDevice.setup(
"mode: 'Serial'");
463 if (occa::io::exists(MFEM_INSTALL_DIR
"/include/mfem/"))
465 mfemDir = MFEM_INSTALL_DIR
"/include/mfem/";
467 else if (occa::io::exists(MFEM_SOURCE_DIR))
469 mfemDir = MFEM_SOURCE_DIR;
473 MFEM_ABORT(
"Cannot find OCCA kernels in MFEM_INSTALL_DIR or MFEM_SOURCE_DIR");
476 occa::io::addLibraryPath(
"mfem", mfemDir);
477 occa::loadKernels(
"mfem");
479 MFEM_CONTRACT_VAR(dev);
480 MFEM_ABORT(
"the OCCA backends require MFEM built with MFEM_USE_OCCA=YES");
484 static void CeedDeviceSetup(
const char* ceed_spec)
487 CeedInit(ceed_spec, &internal::ceed);
488 const char *ceed_backend;
489 CeedGetResource(internal::ceed, &ceed_backend);
490 if (strcmp(ceed_spec, ceed_backend) && strcmp(ceed_spec,
"/cpu/self") &&
491 strcmp(ceed_spec,
"/gpu/hip"))
494 "libCEED is not using the requested backend!!!\n"
495 "WARNING!!!\n" << std::endl;
498 CeedSetErrorHandler(internal::ceed, CeedErrorStore);
501 MFEM_CONTRACT_VAR(ceed_spec);
505 void Device::Setup(
const int device_id)
507 MFEM_VERIFY(ngpu == -1,
"the mfem::Device is already configured!");
511 #ifndef MFEM_USE_CUDA
513 "the CUDA backends require MFEM built with MFEM_USE_CUDA=YES");
517 "the HIP backends require MFEM built with MFEM_USE_HIP=YES");
519 #ifndef MFEM_USE_RAJA
521 "the RAJA backends require MFEM built with MFEM_USE_RAJA=YES");
523 #ifndef MFEM_USE_OPENMP
525 "the OpenMP and RAJA OpenMP backends require MFEM built with"
526 " MFEM_USE_OPENMP=YES");
528 #ifndef MFEM_USE_CEED
530 "the CEED backends require MFEM built with MFEM_USE_CEED=YES");
535 MFEM_VERIFY(ceed_cpu + ceed_cuda + ceed_hip <= 1,
536 "Only one CEED backend can be enabled at a time!");
541 { RajaDeviceSetup(dev, ngpu); }
548 CeedDeviceSetup(
"/cpu/self");
552 CeedDeviceSetup(device_option);
560 CeedDeviceSetup(
"/gpu/cuda/gen");
564 CeedDeviceSetup(device_option);
571 CeedDeviceSetup(
"/gpu/hip");
575 CeedDeviceSetup(device_option);
std::unordered_map< const RestrKey, CeedElemRestriction, RestrHash > RestrMap
static bool IsConfigured()
Return true if Configure() has been called previously.
[device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES and MFEM_USE_CUDA = YES.
Host memory; aligned at 64 bytes.
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
[host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
Device memory; using CUDA or HIP *Malloc and *Free.
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
[device] CEED CUDA backend working together with the CUDA backend. Enabled when MFEM_USE_CEED = YES a...
[host] RAJA OpenMP backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_OPENMP = YES...
Biwise-OR of all HIP backends.
Host memory; allocated from a "host-debug" pool.
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 Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
[device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_CUDA = YES.
int CuGetDeviceCount()
Get the number of CUDA devices.
void Configure(const std::string &device, const int dev=0)
Configure the Device backends.
Host memory; aligned at 32 bytes.
Device()
Default constructor. Unless Configure() is called later, the default Backend::CPU will be used...
Id
In the documentation below, we use square brackets to indicate the type of the backend: host or devic...
[host] OCCA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_OCCA = YES...
Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
static void SetMemoryTypes(MemoryType h_mt, MemoryType d_mt)
Set the default host and device MemoryTypes, h_mt and d_mt.
void Destroy()
Free all the device memories.
[host] RAJA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_RAJA = YES...
std::unordered_map< const BasisKey, CeedBasis, BasisHash > BasisMap
[host] Default CPU backend: sequential execution on each MPI rank.
Biwise-OR of all CUDA backends.
MemoryType
Memory types supported by MFEM.
[host] CEED CPU backend. GPU backends can still be used, but with expensive memory transfers...
[host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
bool IsDeviceMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::DEVICE.
MemoryManager mm
The (single) global memory manager object.
Host memory; using new[] and delete[].
[device] CEED HIP backend working together with the HIP backend. Enabled when MFEM_USE_CEED = YES and...
Biwise-OR of all OCCA backends.
Biwise-OR of all RAJA backends.
[device] RAJA HIP backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_HIP = YES.
Biwise-OR of all device backends.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Bitwise-OR of all CEED backends.
[device] HIP backend. Enabled when MFEM_USE_HIP = YES.
[device] CUDA backend. Enabled when MFEM_USE_CUDA = YES.
[device] Debug backend: host memory is READ/WRITE protected while a device is in use. It allows to test the "device" code-path (using separate host/device memory pools and host <-> device transfers) without any GPU hardware. As 'DEBUG' is sometimes used as a macro, _DEVICE has been added to avoid conflicts.