YAKL
YAKL_ArrayBase.h
Go to the documentation of this file.
1 
7 #pragma once
8 // Included by YAKL_Array.h
9 
11 namespace yakl {
12 
13  // This implements all functionality used by all dynamically allocated arrays
23  template <class T, int rank, int myMem, int myStyle>
24  class ArrayBase {
25  public:
26 
28  typedef typename std::remove_cv<T>::type type;
30  typedef T value_type;
32  typedef typename std::add_const<type>::type const_value_type;
34  typedef typename std::remove_const<type>::type non_const_value_type;
35 
37  T * myData; // Pointer to the flattened internal data
39  index_t dimension[rank]; // Sizes of the 8 possible dimensions
41  int * refCount; // Pointer shared by multiple copies of this Array to keep track of allcation / free
42  #ifdef YAKL_DEBUG
43 
44  char const * myname; // Label for debug printing. Only stored if debugging is turned on
45  #endif
46  #ifdef YAKL_ENABLE_STREAMS
48  #else
49  // This only exists to void extra data in the Array classes
51  struct StreamListDummy {
52  static bool constexpr empty() { return true; }
53  void push_back( Stream stream ) { }
54  int size() { return 0; }
55  Stream operator[] (int i) { return Stream(); }
56  };
57  StreamListDummy stream_dependencies;
58  #endif
59 
60 
68  if constexpr (streams_enabled) {
69  if (use_pool()) stream_dependencies.push_back(stream);
70  }
71  }
72 
73 
76  void add_stream_dependencies(std::vector<Stream> streams) {
77  if constexpr (streams_enabled) {
78  if (use_pool()) {
79  for (int i=0; i < streams.size(); i++) { stream_dependencies.push_back(streams[i]); }
80  }
81  }
82  }
83 
84 
86  void create_inform() {
87  #ifdef YAKL_VERBOSE
88  std::string msg = "Allocating ";
89  if constexpr (myMem == memHost) {
90  msg += std::string("host, ");
91  } else {
92  msg += std::string("device, ");
93  }
94  if constexpr (myStyle == styleC) {
95  msg += std::string("C-style, ");
96  } else {
97  msg += std::string("Fortran-style, ");
98  }
99  msg += std::string("rank ") + std::to_string(rank) + std::string(" Array");
100  msg += std::string(" of size ") + std::to_string(totElems()*sizeof(T)) + std::string(" bytes");
101  verbose_inform(msg,this->label());
102  #endif
103  }
104 
105 
107  void destroy_inform() {
108  #ifdef YAKL_VERBOSE
109  std::string msg = "Deallocating ";
110  if constexpr (myMem == memHost) {
111  msg += std::string("host, ");
112  } else {
113  msg += std::string("device, ");
114  }
115  if constexpr (myStyle == styleC) {
116  msg += std::string("C-style, ");
117  } else {
118  msg += std::string("Fortran-style, ");
119  }
120  msg += std::string("rank ") + std::to_string(rank) + std::string(" Array");
121  verbose_inform(msg,this->label());
122  #endif
123  }
124 
125 
127  template <class ARR>
128  void copy_inform(ARR const &dest) const {
129  #ifdef YAKL_VERBOSE
130  std::string msg = "Initiating ";
131  if (myMem == memHost ) msg += std::string("host to ");
132  if (myMem == memDevice) msg += std::string("device to ");
133  if (dest.get_memory_space() == memHost ) msg += std::string("host memcpy of ");
134  if (dest.get_memory_space() == memDevice) msg += std::string("device memcpy of ");
135  msg += std::to_string(totElems()*sizeof(T)) + std::string(" bytes");
136  if (label() != "") msg += " from Array labeled \"" + std::string(label()) + std::string("\"");
137  if (dest.label() != "") msg += " to Array labeled \"" + std::string(dest.label()) + std::string("\"");
138  verbose_inform(msg);
139  #endif
140  }
141 
142 
143  // Deep copy this array's contents to another array that's on the host
149  template <int theirRank, int theirStyle>
150  inline void deep_copy_to(Array<typename std::remove_cv<T>::type,theirRank,memHost,theirStyle> const &lhs , Stream stream = Stream()) const {
151  #ifdef YAKL_VERBOSE
152  copy_inform(lhs);
153  #endif
154  #ifdef YAKL_DEBUG
155  if (this->totElems() != lhs.totElems()) { yakl_throw("ERROR: deep_copy_to with different number of elements"); }
156  if (this->myData == nullptr || lhs.myData == nullptr) { yakl_throw("ERROR: deep_copy_to with nullptr"); }
157  #endif
158  if (myMem == memHost) { memcpy_host_to_host ( lhs.myData , this->myData , this->totElems() ); }
159  else { memcpy_device_to_host( lhs.myData , this->myData , this->totElems() , stream ); }
160  #ifdef YAKL_AUTO_FENCE
161  fence();
162  #endif
163  }
164 
165 
166  // Deep copy this array's contents to another array that's on the device
172  template <int theirRank, int theirStyle>
173  inline void deep_copy_to(Array<typename std::remove_cv<T>::type,theirRank,memDevice,theirStyle> const &lhs , Stream stream = Stream()) const {
174  #ifdef YAKL_VERBOSE
175  copy_inform(lhs);
176  #endif
177  #ifdef YAKL_DEBUG
178  if (this->totElems() != lhs.totElems()) { yakl_throw("ERROR: deep_copy_to with different number of elements"); }
179  if (this->myData == nullptr || lhs.myData == nullptr) { yakl_throw("ERROR: deep_copy_to with nullptr"); }
180  #endif
181  if (myMem == memHost) { memcpy_host_to_device ( lhs.myData , this->myData , this->totElems() , stream ); }
182  else { memcpy_device_to_device( lhs.myData , this->myData , this->totElems() , stream ); }
183  #ifdef YAKL_AUTO_FENCE
184  fence();
185  #endif
186  }
187 
188 
189  /* ACCESSORS */
191  YAKL_INLINE int get_rank() const { return rank; }
194  index_t tot = this->dimension[0];
195  for (int i=1; i<rank; i++) { tot *= this->dimension[i]; }
196  return tot;
197  }
201  YAKL_INLINE index_t totElems() const { return get_totElems(); }
203  YAKL_INLINE index_t size() const { return get_totElems(); }
205  YAKL_INLINE T *data() const { return this->myData; }
207  YAKL_INLINE T *get_data() const { return this->myData; }
209  YAKL_INLINE T *begin() const { return this->myData; }
211  YAKL_INLINE T *end() const { return begin() + size(); }
213  YAKL_INLINE bool span_is_contiguous() const { return true; }
215  YAKL_INLINE bool initialized() const { return this->myData != nullptr; }
217  YAKL_INLINE int get_memory_space() const { return myMem == memHost ? memHost : memDevice; }
218  const char* label() const {
219  #ifdef YAKL_DEBUG
220  return this->myname;
221  #else
222  return "\"Unlabeled: YAKL_DEBUG CPP macro not defined\"";
223  #endif
224  }
231  inline int use_count() const {
232  if (this->refCount != nullptr) { return *(this->refCount); }
233  else { return 0; }
234  }
235 
236 
237  // Allocate the array and the reference counter (if owned)
239  template <class TLOC=T, typename std::enable_if< ! std::is_const<TLOC>::value , int >::type = 0>
240  inline void allocate() {
241  // static_assert( std::is_arithmetic<T>() || myMem == memHost ,
242  // "ERROR: You cannot use non-arithmetic types inside owned Arrays on the device" );
243  yakl_mtx_lock();
244  this->refCount = new int;
245  (*(this->refCount)) = 1;
246  if (myMem == memDevice) {
247  this->myData = (T *) alloc_device( this->totElems()*sizeof(T) , this->label() );
248  } else {
249  this->myData = new T[this->totElems()];
250  }
251  #ifdef YAKL_VERBOSE
252  this->create_inform();
253  #endif
254  yakl_mtx_unlock();
255  }
256 
257 
258  // Decrement the reference counter (if owned), and if it's zero after decrement, then deallocate the data
259  // For const types, the pointer must be const casted to a non-const type before deallocation
267  template <class TLOC=T, typename std::enable_if< std::is_const<TLOC>::value , int >::type = 0>
268  inline void deallocate() {
269  yakl_mtx_lock();
270  typedef typename std::remove_cv<T>::type T_non_const;
271  T_non_const *data = const_cast<T_non_const *>(this->myData);
272  if (this->refCount != nullptr) {
273  (*(this->refCount))--;
274 
275  if (*this->refCount == 0) {
276  #ifdef YAKL_VERBOSE
277  destroy_inform();
278  #endif
279  delete this->refCount;
280  this->refCount = nullptr;
281  if (this->totElems() > 0) {
282  if (myMem == memDevice) {
283  if (streams_enabled && use_pool() && get_yakl_instance().device_allocators_are_default && (! stream_dependencies.empty()) ) {
284  std::vector<Event> event_dependencies;
285  for (int i=0; i < stream_dependencies.size(); i++) {
286  event_dependencies.push_back( record_event(stream_dependencies[i]) );
287  }
288  get_yakl_instance().pool.free_with_event_dependencies( data , event_dependencies , this->label() );
289  } else {
290  free_device(data,this->label());
291  }
292  } else {
293  delete[] data;
294  }
295  this->myData = nullptr;
296  }
297  }
298 
299  }
300  yakl_mtx_unlock();
301  }
302 
303 
304  // Decrement the reference counter (if owned), and if it's zero after decrement, then deallocate the data
306  template <class TLOC=T, typename std::enable_if< ! std::is_const<TLOC>::value , int >::type = 0>
307  inline void deallocate() {
308  yakl_mtx_lock();
309  if (this->refCount != nullptr) {
310  (*(this->refCount))--;
311 
312  if (*this->refCount == 0) {
313  #ifdef YAKL_VERBOSE
314  destroy_inform();
315  #endif
316  delete this->refCount;
317  this->refCount = nullptr;
318  if (this->totElems() > 0) {
319  if (myMem == memDevice) {
320  if (streams_enabled && use_pool() && get_yakl_instance().device_allocators_are_default && (! stream_dependencies.empty()) ) {
321  std::vector<Event> event_dependencies;
322  for (int i=0; i < stream_dependencies.size(); i++) {
323  event_dependencies.push_back( record_event(stream_dependencies[i]) );
324  }
325  get_yakl_instance().pool.free_with_event_dependencies( this->myData , event_dependencies , this->label() );
326  } else {
327  free_device(this->myData,this->label());
328  }
329  } else {
330  delete[] this->myData;
331  }
332  this->myData = nullptr;
333  }
334  }
335 
336  }
337  yakl_mtx_unlock();
338  }
339 
340 
341  // Print the array contents
343  inline friend std::ostream &operator<<(std::ostream& os, Array<T,rank,myMem,myStyle> const &v) {
344  os << "For Array labeled: " << v.label() << "\n";
345  os << "Number of Dimensions: " << rank << "\n";
346  os << "Total Number of Elements: " << v.totElems() << "\n";
347  os << "Dimension Sizes: ";
348  for (int i=0; i<rank; i++) {
349  os << v.dimension[i] << ", ";
350  }
351  os << "\n";
352  const_value_type *local = v.myData;
353  non_const_value_type *from_dev;
354  if (myMem == memDevice) {
355  from_dev = new non_const_value_type[v.totElems()];
356  #ifdef YAKL_ENABLE_STREAMS
357  fence();
358  #endif
359  memcpy_device_to_host( from_dev , v.myData , v.totElems() );
360  fence();
361  local = from_dev;
362  }
363  for (index_t i=0; i<v.totElems(); i++) {
364  os << local[i] << " ";
365  }
366  if (myMem == memDevice) {
367  delete[] from_dev;
368  }
369  os << "\n";
370  return os;
371  }
372 
373 
374  };
375 
376 }
378 
379 
yakl::ArrayBase::get_memory_space
YAKL_INLINE int get_memory_space() const
Returns this array object's string label if the YAKL_DEBUG CPP macro is defined. Otherwise,...
Definition: YAKL_ArrayBase.h:217
yakl::memDevice
constexpr int memDevice
Specifies a device memory address space for a yakl::Array object.
Definition: YAKL_memory_spaces.h:13
yakl::ArrayBase::value_type
T value_type
This is the type T exactly as it was defined upon array object creation.
Definition: YAKL_ArrayBase.h:30
yakl::ArrayBase::data
YAKL_INLINE T * data() const
Returns the raw data pointer of this array object.
Definition: YAKL_ArrayBase.h:205
yakl::ArrayBase::initialized
YAKL_INLINE bool initialized() const
Returns whether this array object has is in an initialized / allocated state.
Definition: YAKL_ArrayBase.h:215
yakl::ArrayBase::get_rank
YAKL_INLINE int get_rank() const
Returns the number of dimensions in this array object.
Definition: YAKL_ArrayBase.h:191
yakl::ArrayBase::operator<<
friend std::ostream & operator<<(std::ostream &os, Array< T, rank, myMem, myStyle > const &v)
Allows the user to std::cout << this_array_object;. This works even for yakl::memDevice array objects...
Definition: YAKL_ArrayBase.h:343
yakl::Stream
Implements the functionality of a stream for parallel kernel execution. If the Stream::create() metho...
Definition: YAKL_streams_events.h:394
yakl::ArrayBase::deep_copy_to
void deep_copy_to(Array< typename std::remove_cv< T >::type, theirRank, memHost, theirStyle > const &lhs, Stream stream=Stream()) const
[ASYNCHRONOUS] [DEEP_COPY] Copy this array's contents to a yakl::memHost array.
Definition: YAKL_ArrayBase.h:150
yakl::ArrayBase::label
const char * label() const
Definition: YAKL_ArrayBase.h:218
yakl::ArrayBase::non_const_value_type
std::remove_const< type >::type non_const_value_type
This is the type T with const removed from it (if the original type has volatile, then so will this t...
Definition: YAKL_ArrayBase.h:34
yakl::ArrayBase::totElems
YAKL_INLINE index_t totElems() const
Returns the total number of elements in this array object.
Definition: YAKL_ArrayBase.h:201
yakl::ArrayBase::span_is_contiguous
YAKL_INLINE bool span_is_contiguous() const
Always true. yakl::Array objects are always contiguous in memory with no padding.
Definition: YAKL_ArrayBase.h:213
yakl::ArrayBase::const_value_type
std::add_const< type >::type const_value_type
This is the type T with const added to it (if the original type has volatile, then so will this type.
Definition: YAKL_ArrayBase.h:32
yakl::use_pool
bool use_pool()
If true, then the pool allocator is being used for all device allocations.
Definition: YAKL_allocators.h:16
yakl::ArrayBase::begin
YAKL_INLINE T * begin() const
Returns pointer to beginning of the data.
Definition: YAKL_ArrayBase.h:209
__YAKL_NAMESPACE_WRAPPER_END__
#define __YAKL_NAMESPACE_WRAPPER_END__
Definition: YAKL.h:20
yakl::memcpy_host_to_device
void memcpy_host_to_device(T1 *dst, T2 *src, index_t elems, Stream stream=Stream())
[USE AT YOUR OWN RISK]: memcpy the specified number of elements from host to device
Definition: YAKL_mem_transfers.h:85
yakl::free_device
void free_device(void *ptr, char const *label)
Free on the device using YAKL's device deallocator.
Definition: YAKL_allocators.h:237
yakl::streams_enabled
constexpr bool streams_enabled
If the CPP Macro YAKL_ENABLE_STREAMS is defined, then this bool is set to true
Definition: YAKL_streams_events.h:11
yakl::ArrayBase::add_stream_dependency
void add_stream_dependency(Stream stream)
Declare a dependency on the passed stream.
Definition: YAKL_ArrayBase.h:67
__YAKL_NAMESPACE_WRAPPER_BEGIN__
#define __YAKL_NAMESPACE_WRAPPER_BEGIN__
Definition: YAKL.h:19
YAKL_INLINE
#define YAKL_INLINE
Used to decorate functions called from kernels (parallel_for and parallel_outer) or from CPU function...
Definition: YAKL_defines.h:140
yakl::ArrayBase::add_stream_dependencies
void add_stream_dependencies(std::vector< Stream > streams)
Declare a dependencies on the multiple streams at one time.
Definition: YAKL_ArrayBase.h:76
yakl::ArrayBase::use_count
int use_count() const
Returns how many array objects share this pointer if owned; or 0 if unowned.
Definition: YAKL_ArrayBase.h:231
yakl::fence
void fence()
Block the host code until all device code has completed.
Definition: YAKL_fence.h:16
yakl::StreamList
Implements a list of Stream objects. Needs to store a pointer to avoid construction on the device sin...
Definition: YAKL_streams_events.h:446
yakl::ArrayBase::get_data
YAKL_INLINE T * get_data() const
Returns the raw data pointer of this array object.
Definition: YAKL_ArrayBase.h:207
yakl::ArrayBase::get_elem_count
YAKL_INLINE index_t get_elem_count() const
Returns the total number of elements in this array object.
Definition: YAKL_ArrayBase.h:199
yakl::index_t
unsigned int index_t
Definition: YAKL.h:41
yakl::yakl_throw
YAKL_INLINE void yakl_throw(const char *msg)
Throw an error message. Works from the host or device.
Definition: YAKL_error.h:17
yakl::styleC
constexpr int styleC
Template parameter for yakl::Array that specifies it should follow C-style behavior.
Definition: YAKL_Array.h:20
yakl::memcpy_host_to_host
void memcpy_host_to_host(T1 *dst, T2 *src, index_t elems)
[USE AT YOUR OWN RISK]: memcpy the specified number of elements on the host
Definition: YAKL_mem_transfers.h:20
yakl::ArrayBase::get_totElems
YAKL_INLINE index_t get_totElems() const
Returns the total number of elements in this array object.
Definition: YAKL_ArrayBase.h:193
yakl::record_event
Event record_event(Stream stream=Stream())
Create, record, and return an event using the given stream.
Definition: YAKL_streams_events.h:438
yakl::ArrayBase::type
std::remove_cv< T >::type type
This is the type T without const and volatile modifiers.
Definition: YAKL_ArrayBase.h:28
yakl::Array
This declares the yakl::Array class. Please see the yakl::styleC and yakl::styleFortran template spec...
Definition: YAKL_Array.h:40
yakl::memcpy_device_to_device
void memcpy_device_to_device(T1 *dst, T2 *src, index_t elems, Stream stream=Stream())
[USE AT YOUR OWN RISK]: memcpy the specified number of elements on the device
Definition: YAKL_mem_transfers.h:119
yakl::alloc_device
void * alloc_device(size_t bytes, char const *label)
Allocate on the device using YAKL's device allocator.
Definition: YAKL_allocators.h:234
yakl::ArrayBase::deallocate
void deallocate()
If owned, decrement the reference counter; if ref counter reaches zero, deallocate memory; If non-own...
Definition: YAKL_ArrayBase.h:268
yakl::ArrayBase::size
YAKL_INLINE index_t size() const
Returns the total number of elements in this array object.
Definition: YAKL_ArrayBase.h:203
yakl
yakl::memcpy_device_to_host
void memcpy_device_to_host(T1 *dst, T2 *src, index_t elems, Stream stream=Stream())
[USE AT YOUR OWN RISK]: memcpy the specified number of elements from device to host
Definition: YAKL_mem_transfers.h:51
yakl::ArrayBase::end
YAKL_INLINE T * end() const
Returns pointer to end of the data.
Definition: YAKL_ArrayBase.h:211
yakl::ArrayBase::stream_dependencies
StreamListDummy stream_dependencies
Definition: YAKL_ArrayBase.h:57
yakl::ArrayBase
This class implements functionality common to both yakl::styleC and yakl::styleFortran Array objects.
Definition: YAKL_ArrayBase.h:24
yakl::memHost
constexpr int memHost
Specifies a device memory address space for a yakl::Array object.
Definition: YAKL_memory_spaces.h:15
yakl::ArrayBase::deep_copy_to
void deep_copy_to(Array< typename std::remove_cv< T >::type, theirRank, memDevice, theirStyle > const &lhs, Stream stream=Stream()) const
[ASYNCHRONOUS] [DEEP_COPY] Copy this array's contents to a yakl::memDevice array.
Definition: YAKL_ArrayBase.h:173