openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
target.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // openCARP is an open cardiac electrophysiology simulator.
3 //
4 // Copyright (C) 2020 openCARP project
5 //
6 // This program is licensed under the openCARP Academic Public License (APL)
7 // v1.0: You can use and redistribute it and/or modify it in non-commercial
8 // academic environments under the terms of APL as published by the openCARP
9 // project v1.0, or (at your option) any later version. Commercial use requires
10 // a commercial license (info@opencarp.org).
11 //
12 // This program is distributed without any warranty; see the openCARP APL for
13 // more details.
14 //
15 // You should have received a copy of the openCARP APL along with this program
16 // and can find it online: http://www.opencarp.org/license
17 // ----------------------------------------------------------------------------
18 
25 #ifndef TARGETS_H
26 #define TARGETS_H
27 
28 #include <stdexcept>
29 #include <iostream>
30 #include <string>
31 #include <cstring>
32 #ifdef HAS_ROCM_MODEL
33 #include <hip/hip_runtime.h>
34 #endif
35 #ifdef HAS_CUDA_MODEL
36 #include <cuda_runtime.h>
37 #endif
38 
39 namespace limpet
40 {
41 
45 enum Target {
46  AUTO = -2, // !< special value for chosing the target automatically
47  UNKNOWN = -1,
48  CPU,
53 };
54 
62 Target get_target_from_string(std::string const str);
63 
71 std::string get_string_from_target(Target const target);
72 
77 std::string get_target_list_string();
78 
84 bool is_gpu(Target const target);
85 
94 bool is_concrete(Target const target);
95 
103 template <typename T>
105  typedef T value_type;
106 
114  TargetAllocator(Target target, bool always_managed = false) : _target(target), _always_managed(always_managed) {
115  if (!is_concrete(target)) {
116  throw std::invalid_argument("attempting to construct a TargetAllocator with an invalid target");
117  }
118  }
119 
125  Target get_target() const {
126  return this->_target;
127  }
128 
134  void set_target(Target new_target) {
135  if (!is_concrete(new_target)) {
136  throw std::invalid_argument("attempting to set TargetAllocator to an invalid target");
137  }
138  else {
139  this->_target = new_target;
140  }
141  }
142 
154  T* allocate(std::size_t n, bool do_zero = true) {
155  using type = typename std::conditional<std::is_void<T>::value, char, T>::type;
156  T* ptr = nullptr;
157  std::size_t n_bytes = n * sizeof(type);
158  switch (this->_target) {
159  case Target::MLIR_CPU:
160  case Target::CPU: {
161  std::allocator<type> std_alloc;
162  ptr = std_alloc.allocate(n);
163  // Explicitely cast the pointer to silence warning on setting a
164  // dynamic class object to 0.
165  // The memory allocated by this function should be initialized after,
166  // for example by using a placement new
167  if(do_zero)
168  std::memset((void *) ptr, 0, n_bytes);
169 
170  break;
171  }
172  case Target::MLIR_ROCM:
173 #ifdef HAS_ROCM_MODEL
174  {
175  T* hip_ptr;
176  hipError_t error;
177  // Force usage of HIP managed memory even if it's not supported.
178  // This should still preserve the semantics of managed memory, but
179  // is slower. It can be useful to allocate small shared data between CPU
180  // and GPU.
181  if (this->_always_managed) {
182  error = hipMallocManaged(&hip_ptr, n_bytes);
183  }
184  else {
185  int device;
186  int managed_memory = 0;
187  hipGetDevice(&device);
188  hipDeviceGetAttribute(&managed_memory, hipDeviceAttributeManagedMemory, device);
189  // Check for HIP managed memory and fallback to a normal hipMalloc if
190  // it's not supported. It should still allow the CPU to write to GPU memory.
191  if (managed_memory) {
192  error = hipMallocManaged(&hip_ptr, n_bytes);
193  }
194  else {
195  error = hipMalloc(&hip_ptr, n_bytes);
196  }
197  }
198  if (error != hipSuccess) {
199  throw std::runtime_error(hipGetErrorString(error));
200  }
201  error = hipMemset(hip_ptr, 0, n_bytes);
202  if (error != hipSuccess) {
203  throw std::runtime_error(hipGetErrorString(error));
204  }
205  ptr = hip_ptr;
206  }
207 #else
208  throw std::invalid_argument("target MLIR_ROCM is unavailable");
209 #endif
210  break;
211  case Target::MLIR_CUDA:
212 #ifdef HAS_CUDA_MODEL
213  {
214  T* cuda_ptr;
215  cudaError_t error;
216  // Always use unified memory for CUDA
217  error = cudaMallocManaged(&cuda_ptr, n_bytes);
218  if (error != cudaSuccess) {
219  throw std::runtime_error(cudaGetErrorString(error));
220  }
221  error = cudaMemset(cuda_ptr, 0, n_bytes);
222  if (error != cudaSuccess) {
223  throw std::runtime_error(cudaGetErrorString(error));
224  }
225  ptr = cuda_ptr;
226  }
227 #else
228  throw std::invalid_argument("target MLIR_CUDA is unavailable");
229 #endif
230  break;
231  default:
232  throw std::invalid_argument("unknown allocation target");
233  }
234  return ptr;
235  }
236 
237  // Size is not needed
246  void deallocate(T* ptr, std::size_t n = 0) {
247  using type = typename std::conditional<std::is_void<T>::value, char, T>::type;
248  switch (this->_target) {
249  case Target::MLIR_CPU:
250  case Target::CPU: {
251  std::allocator<type> std_alloc;
252  std_alloc.deallocate((type*) ptr, n);
253  }
254  break;
255  case Target::MLIR_ROCM:
256 #ifdef HAS_ROCM_MODEL
257  {
258  hipError_t error;
259  error = hipFree(ptr);
260  if (error != hipSuccess) {
261  throw std::runtime_error(hipGetErrorString(error));
262  }
263  }
264 #else
265  throw std::invalid_argument("target MLIR_ROCM is unavailable");
266 #endif
267  break;
268  case Target::MLIR_CUDA:
269 #ifdef HAS_CUDA_MODEL
270  {
271  cudaError_t error;
272  error = cudaFree(ptr);
273  if (error != cudaSuccess) {
274  throw std::runtime_error(cudaGetErrorString(error));
275  }
276  }
277 #else
278  throw std::invalid_argument("target MLIR_CUDA is unavailable");
279 #endif
280  break;
281  default:
282  throw std::invalid_argument("unknown allocation target");
283  }
284  }
285 
286  private:
287  Target _target;
288  bool _always_managed;
289 };
290 
299 template<typename T>
300 T* allocate_on_target(Target target, std::size_t n, bool always_managed = false, bool do_zero = true) {
301  TargetAllocator<T> alloc(target, always_managed);
302  return alloc.allocate(n, do_zero);
303 }
313 template<typename T>
314 void deallocate_on_target(Target target, T* ptr) {
315  TargetAllocator<T> alloc(target);
316  return alloc.deallocate(ptr);
317 }
318 
319 } // namespace limpet
320 
321 #endif // TARGETS_H
T * allocate_on_target(Target target, std::size_t n, bool always_managed=false, bool do_zero=true)
Utility function for allocating memory on a target. See TargetAllocator.
Definition: target.h:300
bool is_concrete(Target const target)
Checks if target is a real, concrete target.
Definition: target.cc:68
Target
enum that represents different targets to run ionic models on.
Definition: target.h:45
@ AUTO
Definition: target.h:46
@ MLIR_ROCM
ROCM code for AMD GPUs generated with MLIR.
Definition: target.h:50
@ CPU
baseline CPU model generated with the original opencarp code generator
Definition: target.h:48
@ UNKNOWN
special value to handle unknown targets
Definition: target.h:47
@ MLIR_CUDA
CUDA code for NVIDIA GPUs generated with MLIR.
Definition: target.h:51
@ N_TARGETS
a token to indicate the maximum number of targets
Definition: target.h:52
@ MLIR_CPU
vectorized CPU code generated with MLIR
Definition: target.h:49
std::string get_string_from_target(Target const target)
Get a string representation of a given target.
Definition: target.cc:46
std::string get_target_list_string()
Returns a string containing the list of available targets.
Definition: target.cc:55
bool is_gpu(Target const target)
Checks if this is a GPU target.
Definition: target.cc:64
void deallocate_on_target(Target target, T *ptr)
Utility function for deallocating memory on a target. See TargetAllocator.
Definition: target.h:314
Target get_target_from_string(std::string const str)
Returns a value from the Target enum from a given string.
Definition: target.cc:36
Allocator structure for dynamically allocating memory on multiple targets.
Definition: target.h:104
T * allocate(std::size_t n, bool do_zero=true)
Allocate memory for type T.
Definition: target.h:154
void deallocate(T *ptr, std::size_t n=0)
Deallocate memory pointed by ptr.
Definition: target.h:246
Target get_target() const
Get the target for this allocator.
Definition: target.h:125
TargetAllocator(Target target, bool always_managed=false)
Construct a TargetAllocator.
Definition: target.h:114
void set_target(Target new_target)
Set a new target for this allocator.
Definition: target.h:134
T value_type
type to allocate
Definition: target.h:105