MFEM  v4.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
device.hpp
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 #ifndef MFEM_DEVICE_HPP
13 #define MFEM_DEVICE_HPP
14 
15 #include "globals.hpp"
16 #include "mem_manager.hpp"
17 
18 namespace mfem
19 {
20 
21 /// MFEM backends.
22 /** Individual backends will generally implement only a subset of the kernels
23  implemented by the default CPU backend. The goal of the backends is to
24  accelerate data-parallel portions of the code and they can use a device
25  memory space (e.g. GPUs) or share the memory space of the host (OpenMP). */
26 struct Backend
27 {
28  /** @brief In the documentation below, we use square brackets to indicate the
29  type of the backend: host or device. */
30  enum Id
31  {
32  /// [host] Default CPU backend: sequential execution on each MPI rank.
33  CPU = 1 << 0,
34  /// [host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
35  OMP = 1 << 1,
36  /// [device] CUDA backend. Enabled when MFEM_USE_CUDA = YES.
37  CUDA = 1 << 2,
38  /** @brief [host] RAJA CPU backend: sequential execution on each MPI rank.
39  Enabled when MFEM_USE_RAJA = YES. */
40  RAJA_CPU = 1 << 3,
41  /** @brief [host] RAJA OpenMP backend. Enabled when MFEM_USE_RAJA = YES
42  and MFEM_USE_OPENMP = YES. */
43  RAJA_OMP = 1 << 4,
44  /** @brief [device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES
45  and MFEM_USE_CUDA = YES. */
46  RAJA_CUDA = 1 << 5,
47  /** @brief [host] OCCA CPU backend: sequential execution on each MPI rank.
48  Enabled when MFEM_USE_OCCA = YES. */
49  OCCA_CPU = 1 << 6,
50  /// [host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
51  OCCA_OMP = 1 << 7,
52  /** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
53  and MFEM_USE_CUDA = YES. */
54  OCCA_CUDA = 1 << 8
55  };
56 
57  /** @brief Additional useful constants. For example, the *_MASK constants can
58  be used with Device::Allows(). */
59  enum
60  {
61  /// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
63 
64  /// Biwise-OR of all CPU backends
66  /// Biwise-OR of all CUDA backends
68  /// Biwise-OR of all OpenMP backends
70  /// Biwise-OR of all device backends
72 
73  /// Biwise-OR of all RAJA backends
75  /// Biwise-OR of all OCCA backends
77  };
78 };
79 
80 
81 /** @brief The MFEM Device class abstracts hardware devices such as GPUs, as
82  well as programming models such as CUDA, OCCA, RAJA and OpenMP. */
83 /** This class represents a "virtual device" with the following properties:
84  - At most one object of this class can be constructed and that object is
85  controlled by its static methods.
86  - If no Device object is constructed, the static methods will use a default
87  global object which is never configured and always uses Backend::CPU.
88  - Once configured, the object cannot be re-configured during the program
89  lifetime.
90  - MFEM classes use this object to determine where (host or device) to
91  perform an operation and which backend implementation to use.
92  - Multiple backends can be configured at the same time; currently, a fixed
93  priority order is used to select a specific backend from the list of
94  configured backends. See the Backend class and the Configure() method in
95  this class for details. */
96 class Device
97 {
98 private:
99  enum MODES {SEQUENTIAL, ACCELERATED};
100 
101  static Device device_singleton;
102 
103  MODES mode;
104  int dev = 0; ///< Device ID of the configured device.
105  int ngpu = -1; ///< Number of detected devices; -1: not initialized.
106  unsigned long backends; ///< Bitwise-OR of all configured backends.
107  /// Set to true during configuration, except in 'device_singleton'.
108  bool destroy_mm;
109 
110  MemoryType mem_type; ///< Current Device MemoryType
111  MemoryClass mem_class; ///< Current Device MemoryClass
112 
113  Device(Device const&);
114  void operator=(Device const&);
115  static Device& Get() { return device_singleton; }
116 
117  /// Setup switcher based on configuration settings
118  void Setup(const int dev = 0);
119 
120  void MarkBackend(Backend::Id b) { backends |= b; }
121 
122  void UpdateMemoryTypeAndClass();
123 
124  /// Enable the use of the configured device in the code that follows.
125  /** After this call MFEM classes will use the backend kernels whenever
126  possible, transferring data automatically to the device, if necessary.
127 
128  If the only configured backend is the default host CPU one, the device
129  will remain disabled.
130 
131  If the device is actually enabled, this method will also update the
132  current MemoryType and MemoryClass. */
133  static void Enable();
134 
135 public:
136  /** @brief Default constructor. Unless Configure() is called later, the
137  default Backend::CPU will be used. */
138  /** @note At most one Device object can be constructed during the lifetime of
139  a program.
140  @note This object should be destroyed after all other MFEM objects that
141  use the Device are destroyed. */
143  : mode(Device::SEQUENTIAL),
144  backends(Backend::CPU),
145  destroy_mm(false),
146  mem_type(MemoryType::HOST),
147  mem_class(MemoryClass::HOST)
148  { }
149 
150  /** @brief Construct a Device and configure it based on the @a device string.
151  See Configure() for more details. */
152  /** @note At most one Device object can be constructed during the lifetime of
153  a program.
154  @note This object should be destroyed after all other MFEM objects that
155  use the Device are destroyed. */
156  Device(const std::string &device, const int dev = 0)
157  : mode(Device::SEQUENTIAL),
158  backends(Backend::CPU),
159  destroy_mm(false),
160  mem_type(MemoryType::HOST),
161  mem_class(MemoryClass::HOST)
162  { Configure(device, dev); }
163 
164  /// Destructor.
165  ~Device();
166 
167  /// Configure the Device backends.
168  /** The string parameter @a device must be a comma-separated list of backend
169  string names (see below). The @a dev argument specifies the ID of the
170  actual devices (e.g. GPU) to use.
171  * The available backends are described by the Backend class.
172  * The string name of a backend is the lowercase version of the
173  Backend::Id enumeration constant with '_' replaced by '-', e.g. the
174  string name of 'RAJA_CPU' is 'raja-cpu'.
175  * The 'cpu' backend is always enabled with lowest priority.
176  * The current backend priority from highest to lowest is: 'occa-cuda',
177  'raja-cuda', 'cuda', 'occa-omp', 'raja-omp', 'omp', 'occa-cpu',
178  'raja-cpu', 'cpu'.
179  * Multiple backends can be configured at the same time.
180  * Only one 'occa-*' backend can be configured at a time.
181  * The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
182  is already enabled. */
183  void Configure(const std::string &device, const int dev = 0);
184 
185  /// Print the configuration of the MFEM virtual device object.
186  void Print(std::ostream &out = mfem::out);
187 
188  /// Return true if Configure() has been called previously.
189  static inline bool IsConfigured() { return Get().ngpu >= 0; }
190 
191  /// Return true if an actual device (e.g. GPU) has been configured.
192  static inline bool IsAvailable() { return Get().ngpu > 0; }
193 
194  /// Return true if any backend other than Backend::CPU is enabled.
195  static inline bool IsEnabled() { return Get().mode == ACCELERATED; }
196 
197  /// The opposite of IsEnabled().
198  static inline bool IsDisabled() { return !IsEnabled(); }
199 
200  /** @brief Return true if any of the backends in the backend mask, @a b_mask,
201  are allowed. */
202  /** This method can be used with any of the Backend::Id constants, the
203  Backend::*_MASK, or combinations of those. */
204  static inline bool Allows(unsigned long b_mask)
205  { return Get().backends & b_mask; }
206 
207  /** @brief Get the current Device MemoryType. This is the MemoryType used by
208  most MFEM classes when allocating memory to be used with device kernels.
209  */
210  static inline MemoryType GetMemoryType() { return Get().mem_type; }
211 
212  /** @brief Get the current Device MemoryClass. This is the MemoryClass used
213  by most MFEM device kernels to access Memory objects. */
214  static inline MemoryClass GetMemoryClass() { return Get().mem_class; }
215 };
216 
217 
218 // Inline Memory access functions using the mfem::Device MemoryClass or
219 // MemoryClass::HOST.
220 
221 /** @brief Get a pointer for read access to @a mem with the mfem::Device
222  MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
223 /** Also, if @a on_dev = true, the device flag of @a mem will be set. */
224 template <typename T>
225 inline const T *Read(const Memory<T> &mem, int size, bool on_dev = true)
226 {
227  if (!on_dev)
228  {
229  return mem.Read(MemoryClass::HOST, size);
230  }
231  else
232  {
233  mem.UseDevice(true);
234  return mem.Read(Device::GetMemoryClass(), size);
235  }
236 }
237 
238 /** @brief Shortcut to Read(const Memory<T> &mem, int size, false) */
239 template <typename T>
240 inline const T *HostRead(const Memory<T> &mem, int size)
241 {
242  return mfem::Read(mem, size, false);
243 }
244 
245 /** @brief Get a pointer for write access to @a mem with the mfem::Device
246  MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
247 /** Also, if @a on_dev = true, the device flag of @a mem will be set. */
248 template <typename T>
249 inline T *Write(Memory<T> &mem, int size, bool on_dev = true)
250 {
251  if (!on_dev)
252  {
253  return mem.Write(MemoryClass::HOST, size);
254  }
255  else
256  {
257  mem.UseDevice(true);
258  return mem.Write(Device::GetMemoryClass(), size);
259  }
260 }
261 
262 /** @brief Shortcut to Write(const Memory<T> &mem, int size, false) */
263 template <typename T>
264 inline const T *HostWrite(const Memory<T> &mem, int size)
265 {
266  return mfem::Write(mem, size, false);
267 }
268 
269 /** @brief Get a pointer for read+write access to @a mem with the mfem::Device
270  MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
271 /** Also, if @a on_dev = true, the device flag of @a mem will be set. */
272 template <typename T>
273 inline T *ReadWrite(Memory<T> &mem, int size, bool on_dev = true)
274 {
275  if (!on_dev)
276  {
277  return mem.ReadWrite(MemoryClass::HOST, size);
278  }
279  else
280  {
281  mem.UseDevice(true);
282  return mem.ReadWrite(Device::GetMemoryClass(), size);
283  }
284 }
285 
286 /** @brief Shortcut to ReadWrite(const Memory<T> &mem, int size, false) */
287 template <typename T>
288 inline const T *HostReadWrite(const Memory<T> &mem, int size)
289 {
290  return mfem::ReadWrite(mem, size, false);
291 }
292 
293 } // mfem
294 
295 #endif // MFEM_DEVICE_HPP
static MemoryClass GetMemoryClass()
Get the current Device MemoryClass. This is the MemoryClass used by most MFEM device kernels to acces...
Definition: device.hpp:214
static bool IsAvailable()
Return true if an actual device (e.g. GPU) has been configured.
Definition: device.hpp:192
[host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
Definition: device.hpp:35
static bool IsConfigured()
Return true if Configure() has been called previously.
Definition: device.hpp:189
~Device()
Destructor.
Definition: device.cpp:54
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device MemoryClass, if on_dev = true...
Definition: device.hpp:249
Device(const std::string &device, const int dev=0)
Construct a Device and configure it based on the device string. See Configure() for more details...
Definition: device.hpp:156
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition: device.cpp:98
static bool IsEnabled()
Return true if any backend other than Backend::CPU is enabled.
Definition: device.hpp:195
static bool IsDisabled()
The opposite of IsEnabled().
Definition: device.hpp:198
void Configure(const std::string &device, const int dev=0)
Configure the Device backends.
Definition: device.cpp:59
Device()
Default constructor. Unless Configure() is called later, the default Backend::CPU will be used...
Definition: device.hpp:142
[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
MFEM backends.
Definition: device.hpp:26
static MemoryType GetMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition: device.hpp:210
Biwise-OR of all OpenMP backends.
Definition: device.hpp:69
[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
const T * Read(const Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read access to mem with the mfem::Device MemoryClass, if on_dev = true...
Definition: device.hpp:225
Biwise-OR of all CUDA backends.
Definition: device.hpp:67
Biwise-OR of all CPU backends.
Definition: device.hpp:65
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:27
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
const T * HostRead(const Memory< T > &mem, int size)
Shortcut to Read(const Memory&lt;T&gt; &amp;mem, int size, false)
Definition: device.hpp:240
T * ReadWrite(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read+write access to mem with the mfem::Device MemoryClass, if on_dev = true...
Definition: device.hpp:273
const T * HostReadWrite(const Memory< T > &mem, int size)
Shortcut to ReadWrite(const Memory&lt;T&gt; &amp;mem, int size, false)
Definition: device.hpp:288
Host memory; using new[] and delete[].
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
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
Class used by MFEM to store pointers to host and/or device memory.
Biwise-OR of all RAJA backends.
Definition: device.hpp:74
const T * HostWrite(const Memory< T > &mem, int size)
Shortcut to Write(const Memory&lt;T&gt; &amp;mem, int size, false)
Definition: device.hpp:264
bool UseDevice() const
Read the internal device flag.
Biwise-OR of all device backends.
Definition: device.hpp:71
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
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
MemoryClass
Memory classes identify subsets of memory types.
Definition: mem_manager.hpp:40
[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