MFEM  v4.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
device.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
3 // reserved. See file COPYRIGHT for details.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability see http://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the GNU Lesser General Public License (as published by the Free
10 // Software Foundation) version 2.1 dated February 1999.
11 
12 #include "forall.hpp"
13 #include "cuda.hpp"
14 #include "occa.hpp"
15 
16 #include <string>
17 #include <map>
18 
19 namespace mfem
20 {
21 
22 // Place the following variables in the mfem::internal namespace, so that they
23 // will not be included in the doxygen documentation.
24 namespace internal
25 {
26 
27 #ifdef MFEM_USE_OCCA
28 // Default occa::device used by MFEM.
29 occa::device occaDevice;
30 #endif
31 
32 // Backends listed by priority, high to low:
33 static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
34 {
38 };
39 
40 // Backend names listed by priority, high to low:
41 static const char *backend_name[Backend::NUM_BACKENDS] =
42 {
43  "occa-cuda", "raja-cuda", "cuda", "occa-omp", "raja-omp", "omp",
44  "occa-cpu", "raja-cpu", "cpu"
45 };
46 
47 } // namespace mfem::internal
48 
49 
50 // Initialize the unique global Device variable.
51 Device Device::device_singleton;
52 
53 
55 {
56  if (destroy_mm) { mm.Destroy(); }
57 }
58 
59 void Device::Configure(const std::string &device, const int dev)
60 {
61  std::map<std::string, Backend::Id> bmap;
62  for (int i = 0; i < Backend::NUM_BACKENDS; i++)
63  {
64  bmap[internal::backend_name[i]] = internal::backend_list[i];
65  }
66  std::string::size_type beg = 0, end;
67  while (1)
68  {
69  end = device.find(',', beg);
70  end = (end != std::string::npos) ? end : device.size();
71  const std::string bname = device.substr(beg, end - beg);
72  std::map<std::string, Backend::Id>::iterator it = bmap.find(bname);
73  MFEM_VERIFY(it != bmap.end(), "invalid backend name: '" << bname << '\'');
74  Get().MarkBackend(it->second);
75  if (end == device.size()) { break; }
76  beg = end + 1;
77  }
78 
79  // OCCA_CUDA needs CUDA or RAJA_CUDA:
81  {
82  Get().MarkBackend(Backend::CUDA);
83  }
84 
85  // Perform setup.
86  Get().Setup(dev);
87 
88  // Enable the device
89  Enable();
90 
91  // Copy all data members from the global 'singleton_device' into '*this'.
92  std::memcpy(this, &Get(), sizeof(Device));
93 
94  // Only '*this' will call the MemoryManager::Destroy() method.
95  destroy_mm = true;
96 }
97 
98 void Device::Print(std::ostream &out)
99 {
100  out << "Device configuration: ";
101  bool add_comma = false;
102  for (int i = 0; i < Backend::NUM_BACKENDS; i++)
103  {
104  if (backends & internal::backend_list[i])
105  {
106  if (add_comma) { out << ','; }
107  add_comma = true;
108  out << internal::backend_name[i];
109  }
110  }
111  out << '\n';
112 }
113 
114 void Device::UpdateMemoryTypeAndClass()
115 {
117  {
118  mem_type = MemoryType::CUDA;
119  mem_class = MemoryClass::CUDA;
120  }
121  else
122  {
123  mem_type = MemoryType::HOST;
124  mem_class = MemoryClass::HOST;
125  }
126 }
127 
128 void Device::Enable()
129 {
130  if (Get().backends & ~Backend::CPU)
131  {
132  Get().mode = Device::ACCELERATED;
133  Get().UpdateMemoryTypeAndClass();
134  }
135 }
136 
137 #ifdef MFEM_USE_CUDA
138 static void DeviceSetup(const int dev, int &ngpu)
139 {
140  MFEM_CUDA_CHECK(cudaGetDeviceCount(&ngpu));
141  MFEM_VERIFY(ngpu > 0, "No CUDA device found!");
142  MFEM_CUDA_CHECK(cudaSetDevice(dev));
143 }
144 #endif
145 
146 static void CudaDeviceSetup(const int dev, int &ngpu)
147 {
148 #ifdef MFEM_USE_CUDA
149  DeviceSetup(dev, ngpu);
150 #endif
151 }
152 
153 static void RajaDeviceSetup(const int dev, int &ngpu)
154 {
155 #ifdef MFEM_USE_CUDA
156  if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
157 #endif
158 }
159 
160 static void OccaDeviceSetup(const int dev)
161 {
162 #ifdef MFEM_USE_OCCA
163  const int cpu = Device::Allows(Backend::OCCA_CPU);
164  const int omp = Device::Allows(Backend::OCCA_OMP);
165  const int cuda = Device::Allows(Backend::OCCA_CUDA);
166  if (cpu + omp + cuda > 1)
167  {
168  MFEM_ABORT("Only one OCCA backend can be configured at a time!");
169  }
170  if (cuda)
171  {
172 #if OCCA_CUDA_ENABLED
173  std::string mode("mode: 'CUDA', device_id : ");
174  internal::occaDevice.setup(mode.append(1,'0'+dev));
175 #else
176  MFEM_ABORT("the OCCA CUDA backend requires OCCA built with CUDA!");
177 #endif
178  }
179  else if (omp)
180  {
181 #if OCCA_OPENMP_ENABLED
182  internal::occaDevice.setup("mode: 'OpenMP'");
183 #else
184  MFEM_ABORT("the OCCA OpenMP backend requires OCCA built with OpenMP!");
185 #endif
186  }
187  else
188  {
189  internal::occaDevice.setup("mode: 'Serial'");
190  }
191 
192  std::string mfemDir;
193  if (occa::io::exists(MFEM_INSTALL_DIR "/include/mfem/"))
194  {
195  mfemDir = MFEM_INSTALL_DIR "/include/mfem/";
196  }
197  else if (occa::io::exists(MFEM_SOURCE_DIR))
198  {
199  mfemDir = MFEM_SOURCE_DIR;
200  }
201  else
202  {
203  MFEM_ABORT("Cannot find OCCA kernels in MFEM_INSTALL_DIR or MFEM_SOURCE_DIR");
204  }
205 
206  occa::io::addLibraryPath("mfem", mfemDir);
207  occa::loadKernels("mfem");
208 #else
209  MFEM_ABORT("the OCCA backends require MFEM built with MFEM_USE_OCCA=YES");
210 #endif
211 }
212 
213 void Device::Setup(const int device)
214 {
215  MFEM_VERIFY(ngpu == -1, "the mfem::Device is already configured!");
216 
217  ngpu = 0;
218  dev = device;
219 
220 #ifndef MFEM_USE_CUDA
221  MFEM_VERIFY(!Allows(Backend::CUDA_MASK),
222  "the CUDA backends require MFEM built with MFEM_USE_CUDA=YES");
223 #endif
224 #ifndef MFEM_USE_RAJA
225  MFEM_VERIFY(!Allows(Backend::RAJA_MASK),
226  "the RAJA backends require MFEM built with MFEM_USE_RAJA=YES");
227 #endif
228 #ifndef MFEM_USE_OPENMP
229  MFEM_VERIFY(!Allows(Backend::OMP|Backend::RAJA_OMP),
230  "the OpenMP and RAJA OpenMP backends require MFEM built with"
231  " MFEM_USE_OPENMP=YES");
232 #endif
233  if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
234  if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
235  // The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
236  if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
237 }
238 
239 } // mfem
[host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
Definition: device.hpp:35
~Device()
Destructor.
Definition: device.cpp:54
Memory types: { CUDA, CUDA_UVM }.
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition: device.cpp:98
void Configure(const std::string &device, const int dev=0)
Configure the Device backends.
Definition: device.cpp:59
[device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES and MFEM_USE_CUDA = YES.
Definition: device.hpp:54
[host] RAJA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_RAJA = YES...
Definition: device.hpp:40
[host] RAJA OpenMP backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_OPENMP = YES...
Definition: device.hpp:43
Number of backends: from (1 &lt;&lt; 0) to (1 &lt;&lt; (NUM_BACKENDS-1)).
Definition: device.hpp:62
[host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
Definition: device.hpp:51
[host] Default CPU backend: sequential execution on each MPI rank.
Definition: device.hpp:33
Biwise-OR of all CUDA backends.
Definition: device.hpp:67
cudaMalloc, cudaFree
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition: device.hpp:204
MemoryManager mm
The (single) global memory manager object.
Host memory; using new[] and delete[].
Biwise-OR of all OCCA backends.
Definition: device.hpp:76
Id
In the documentation below, we use square brackets to indicate the type of the backend: host or devic...
Definition: device.hpp:30
Biwise-OR of all RAJA backends.
Definition: device.hpp:74
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:64
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition: device.hpp:96
[device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES and MFEM_USE_CUDA = YES.
Definition: device.hpp:46
[host] OCCA CPU backend: sequential execution on each MPI rank. Enabled when MFEM_USE_OCCA = YES...
Definition: device.hpp:49
[device] CUDA backend. Enabled when MFEM_USE_CUDA = YES.
Definition: device.hpp:37