LvArray
ChaiBuffer.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021, Lawrence Livermore National Security, LLC and LvArray contributors.
3  * All rights reserved.
4  * See the LICENSE file for details.
5  * SPDX-License-Identifier: (BSD-3-Clause)
6  */
7 
13 #pragma once
14 
15 // Source includes
16 #include "LvArrayConfig.hpp"
17 #include "Macros.hpp"
18 #include "typeManipulation.hpp"
19 #include "arrayManipulation.hpp"
20 #include "system.hpp"
21 #include "bufferManipulation.hpp"
22 
23 // TPL includes
24 #include <chai/ArrayManager.hpp>
25 
26 // System includes
27 #include <mutex>
28 
29 
30 namespace LvArray
31 {
32 
33 namespace internal
34 {
35 
39 inline chai::ArrayManager & getArrayManager()
40 {
41  static chai::ArrayManager & arrayManager = *chai::ArrayManager::getInstance();
42  return arrayManager;
43 }
44 
46 static std::mutex chaiLock;
47 
52 inline chai::ExecutionSpace toChaiExecutionSpace( MemorySpace const space )
53 {
54  if( space == MemorySpace::undefined )
55  return chai::NONE;
56  if( space == MemorySpace::host )
57  return chai::CPU;
58 #if defined(LVARRAY_USE_CUDA)
59  if( space == MemorySpace::cuda || space == MemorySpace::hip )
60  return chai::GPU;
61 #endif
62 
63  LVARRAY_ERROR( "Unrecognized memory space " << static_cast< int >( space ) );
64 
65  return chai::NONE;
66 }
67 
72 inline MemorySpace toMemorySpace( chai::ExecutionSpace const space )
73 {
74  if( space == chai::NONE )
75  return MemorySpace::undefined;
76  if( space == chai::CPU )
77  return MemorySpace::host;
78 #if defined(LVARRAY_USE_CUDA)
79  if( space == chai::GPU )
80  return MemorySpace::cuda;
81 #endif
82 
83  LVARRAY_ERROR( "Unrecognized execution space " << static_cast< int >( space ) );
84 
85  return MemorySpace::undefined;
86 }
87 
88 } // namespace internal
89 
103 template< typename T >
105 {
106 public:
107 
109  using value_type = T;
110 
112  constexpr static bool hasShallowCopy = true;
113 
115  using T_non_const = std::remove_const_t< T >;
116 
122  LVARRAY_HOST_DEVICE inline constexpr
124  m_pointer( nullptr ),
125  m_capacity( 0 ),
126  m_pointerRecord( nullptr )
127  {}
128 
134  ChaiBuffer( bool ):
135  m_pointer( nullptr ),
136  m_capacity( 0 ),
137  m_pointerRecord( new chai::PointerRecord{} )
138  {
139  m_pointerRecord->m_size = 0;
140  setName( "" );
141 
142  for( int space = chai::CPU; space < chai::NUM_EXECUTION_SPACES; ++space )
143  {
144  m_pointerRecord->m_allocators[ space ] = internal::getArrayManager().getAllocatorId( chai::ExecutionSpace( space ) );
145  }
146  }
147 
154  ChaiBuffer( std::initializer_list< MemorySpace > const & spaces,
155  std::initializer_list< umpire::Allocator > const & allocators ):
156  m_pointer( nullptr ),
157  m_capacity( 0 ),
158  m_pointerRecord( new chai::PointerRecord{} )
159  {
160  m_pointerRecord->m_size = 0;
161  setName( "" );
162 
163  LVARRAY_ERROR_IF_NE( spaces.size(), allocators.size() );
164 
165  for( int space = chai::CPU; space < chai::NUM_EXECUTION_SPACES; ++space )
166  {
167  m_pointerRecord->m_allocators[ space ] = internal::getArrayManager().getAllocatorId( chai::ExecutionSpace( space ) );
168  }
169 
170  for( std::size_t i = 0; i < spaces.size(); ++i )
171  {
172  m_pointerRecord->m_allocators[ internal::toChaiExecutionSpace( spaces.begin()[ i ] ) ] = allocators.begin()[ i ].getId();
173  }
174  }
175 
182  LVARRAY_HOST_DEVICE inline
183  ChaiBuffer( ChaiBuffer const & src ):
184  m_pointer( src.m_pointer ),
185  m_capacity( src.m_capacity ),
186  m_pointerRecord( src.m_pointerRecord )
187  {
188  #if defined(LVARRAY_USE_CUDA) && !defined(__CUDA_ARCH__)
189  move( internal::toMemorySpace( internal::getArrayManager().getExecutionSpace() ), true );
190  #endif
191  }
192 
200  LVARRAY_HOST_DEVICE inline
201  ChaiBuffer( ChaiBuffer const & src, std::ptrdiff_t const size ):
202  m_pointer( src.m_pointer ),
203  m_capacity( src.m_capacity ),
204  m_pointerRecord( src.m_pointerRecord )
205  {
206  #if defined(LVARRAY_USE_CUDA) && !defined(__CUDA_ARCH__)
207  moveNested( internal::toMemorySpace( internal::getArrayManager().getExecutionSpace() ), size, true );
208  #else
209  LVARRAY_UNUSED_VARIABLE( size );
210  #endif
211  }
212 
217  LVARRAY_HOST_DEVICE inline constexpr
219  m_pointer( src.m_pointer ),
220  m_capacity( src.m_capacity ),
221  m_pointerRecord( src.m_pointerRecord )
222  {
223  src.m_capacity = 0;
224  src.m_pointer = nullptr;
225  src.m_pointerRecord = nullptr;
226  }
227 
233  template< typename U >
234  LVARRAY_HOST_DEVICE inline constexpr
235  ChaiBuffer( ChaiBuffer< U > const & src ):
236  m_pointer( reinterpret_cast< T * >( src.data() ) ),
237  m_capacity( typeManipulation::convertSize< T, U >( src.capacity() ) ),
238  m_pointerRecord( &src.pointerRecord() )
239  {}
240 
246  LVARRAY_HOST_DEVICE inline LVARRAY_INTEL_CONSTEXPR
247  ChaiBuffer & operator=( ChaiBuffer const & src )
248  {
249  m_capacity = src.m_capacity;
250  m_pointer = src.m_pointer;
251  m_pointerRecord = src.m_pointerRecord;
252  return *this;
253  }
254 
260  LVARRAY_HOST_DEVICE inline LVARRAY_INTEL_CONSTEXPR
262  {
263  m_capacity = src.m_capacity;
264  m_pointer = src.m_pointer;
265  m_pointerRecord = src.m_pointerRecord;
266 
267  src.m_capacity = 0;
268  src.m_pointer = nullptr;
269  src.m_pointerRecord = nullptr;
270 
271  return *this;
272  }
273 
281  void reallocate( std::ptrdiff_t const size, MemorySpace const space, std::ptrdiff_t const newCapacity )
282  {
283  move( space, true );
284  chai::PointerRecord * const newRecord = new chai::PointerRecord{};
285  newRecord->m_size = newCapacity * sizeof( T );
286  newRecord->m_user_callback = m_pointerRecord->m_user_callback;
287 
288  for( int s = chai::CPU; s < chai::NUM_EXECUTION_SPACES; ++s )
289  {
290  newRecord->m_allocators[ s ] = m_pointerRecord->m_allocators[ s ];
291  }
292 
293  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
294 
295  internal::chaiLock.lock();
296  internal::getArrayManager().allocate( newRecord, chaiSpace );
297  internal::chaiLock.unlock();
298 
299  T * const newPointer = static_cast< T * >( newRecord->m_pointers[ chaiSpace ] );
300 
301  if( size > 0 )
302  {
303  LVARRAY_ERROR_IF_NE_MSG( space, MemorySpace::host, "Calling reallocate with a non-zero current size is not yet supporeted for the GPU." );
304  std::ptrdiff_t const overlapAmount = std::min( newCapacity, size );
305  arrayManipulation::uninitializedMove( newPointer, overlapAmount, m_pointer );
306  arrayManipulation::destroy( m_pointer, size );
307  }
308 
309  free();
310  m_capacity = newCapacity;
311  m_pointer = newPointer;
312  m_pointerRecord = newRecord;
313  registerTouch( space );
314  }
315 
320  inline
321  void free()
322  {
323  std::lock_guard< std::mutex > lock( internal::chaiLock );
324  internal::getArrayManager().free( m_pointerRecord );
325  m_capacity = 0;
326  m_pointer = nullptr;
327  m_pointerRecord = nullptr;
328  }
329 
333  LVARRAY_HOST_DEVICE inline constexpr
334  std::ptrdiff_t capacity() const
335  { return m_capacity; }
336 
340  LVARRAY_HOST_DEVICE inline constexpr
341  T * data() const
342  { return m_pointer; }
343 
348  LVARRAY_HOST_DEVICE inline constexpr
349  chai::PointerRecord & pointerRecord() const
350  { return *m_pointerRecord; }
351 
358  template< typename INDEX_TYPE >
359  LVARRAY_HOST_DEVICE inline constexpr
360  T & operator[]( INDEX_TYPE const i ) const
361  { return m_pointer[ i ]; }
362 
370  inline
371  void moveNested( MemorySpace const space, std::ptrdiff_t const size, bool const touch ) const
372  {
373  #if defined(LVARRAY_USE_CUDA)
374  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
375  if( m_pointerRecord == nullptr ||
376  m_capacity == 0 ||
377  chaiSpace == chai::NONE ) return;
378 
379  chai::ExecutionSpace const prevSpace = m_pointerRecord->m_last_space;
380 
381  if( prevSpace == chai::CPU && prevSpace != chaiSpace ) moveInnerData( space, size, touch );
382 
383  move( space, touch );
384 
385  if( prevSpace == chai::GPU && prevSpace != chaiSpace ) moveInnerData( space, size, touch );
386  #else
387  LVARRAY_ERROR_IF_NE( space, MemorySpace::host );
388  LVARRAY_UNUSED_VARIABLE( size );
389  LVARRAY_UNUSED_VARIABLE( touch );
390  #endif
391  }
392 
399  void move( MemorySpace const space, bool const touch ) const
400  {
401  #if defined(LVARRAY_USE_CUDA)
402  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
403  if( m_pointerRecord == nullptr ||
404  m_capacity == 0 ||
405  chaiSpace == chai::NONE ) return;
406 
407  const_cast< T * & >( m_pointer ) =
408  static_cast< T * >( internal::getArrayManager().move( const_cast< T_non_const * >( m_pointer ),
409  m_pointerRecord,
410  chaiSpace ) );
411 
412  if( !std::is_const< T >::value && touch ) m_pointerRecord->m_touched[ chaiSpace ] = true;
413  m_pointerRecord->m_last_space = chaiSpace;
414  #else
415  LVARRAY_ERROR_IF_NE( space, MemorySpace::host );
416  LVARRAY_UNUSED_VARIABLE( touch );
417  #endif
418  }
419 
424  { return internal::toMemorySpace( m_pointerRecord->m_last_space ); }
425 
430  inline constexpr
431  void registerTouch( MemorySpace const space ) const
432  {
433  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
434  m_pointerRecord->m_touched[ chaiSpace ] = true;
435  m_pointerRecord->m_last_space = chaiSpace;
436  }
437 
443  template< typename U=ChaiBuffer< T > >
444  void setName( std::string const & name )
445  {
446  std::string const typeString = system::demangleType< U >();
447  m_pointerRecord->m_user_callback =
448  [name, typeString]( chai::PointerRecord const * const record, chai::Action const act, chai::ExecutionSpace const s )
449  {
450  if( act == chai::ACTION_MOVE )
451  {
452  std::string const size = system::calculateSize( record->m_size );
453  std::string const paddedSize = std::string( 9 - size.size(), ' ' ) + size;
454  char const * const spaceStr = ( s == chai::CPU ) ? "HOST " : "DEVICE";
455  LVARRAY_LOG( "Moved " << paddedSize << " to the " << spaceStr << ": " << typeString << " " << name );
456  }
457  };
458  }
459 
460 private:
461 
471  template< typename U=T_non_const >
472  std::enable_if_t< bufferManipulation::HasMemberFunction_move< U > >
473  moveInnerData( MemorySpace const space, std::ptrdiff_t const size, bool const touch ) const
474  {
475  if( space == MemorySpace::undefined ) return;
476 
477  for( std::ptrdiff_t i = 0; i < size; ++i )
478  {
479  const_cast< T_non_const * >( m_pointer )[ i ].move( space, touch );
480  }
481  }
482 
489  template< typename U=T_non_const >
490  std::enable_if_t< !bufferManipulation::HasMemberFunction_move< U > >
491  moveInnerData( MemorySpace const, std::ptrdiff_t const, bool const ) const
492  {}
493 
495  T * LVARRAY_RESTRICT m_pointer = nullptr;
496 
498  std::ptrdiff_t m_capacity = 0;
499 
501  chai::PointerRecord * m_pointerRecord = nullptr;
502 };
503 
504 } /* namespace LvArray */
#define LVARRAY_UNUSED_VARIABLE(X)
Mark X as an unused variable, used to silence compiler warnings.
Definition: Macros.hpp:51
void free()
Free the data in the buffer but does not destroy any values.
Definition: ChaiBuffer.hpp:321
ChaiBuffer(std::initializer_list< MemorySpace > const &spaces, std::initializer_list< umpire::Allocator > const &allocators)
Construct a ChaiBuffer which uses the specific allocator for each space.
Definition: ChaiBuffer.hpp:154
DISABLE_HD_WARNING LVARRAY_HOST_DEVICE void uninitializedMove(T *const LVARRAY_RESTRICT dst, std::ptrdiff_t const size, T *const LVARRAY_RESTRICT src)
Move construct values from the source to the destination.
Definition: arrayManipulation.hpp:201
void setName(std::string const &name)
Set the name associated with this buffer which is used in the chai callback.
Definition: ChaiBuffer.hpp:444
MemorySpace getPreviousSpace() const
Definition: ChaiBuffer.hpp:423
T value_type
Alias for T used used in the bufferManipulation functions.
Definition: ChaiBuffer.hpp:109
void moveNested(MemorySpace const space, std::ptrdiff_t const size, bool const touch) const
Move the buffer to the given execution space, optionally touching it.
Definition: ChaiBuffer.hpp:371
Contains functions that interact with the system or runtime environment.
static std::mutex chaiLock
chai is not threadsafe so we use a lock to serialize access.
Definition: ChaiBuffer.hpp:46
LVARRAY_HOST_DEVICE ChaiBuffer(ChaiBuffer const &src, std::ptrdiff_t const size)
Copy constructor.
Definition: ChaiBuffer.hpp:201
std::ptrdiff_t m_capacity
The size of the allocation.
Definition: ChaiBuffer.hpp:498
constexpr LVARRAY_HOST_DEVICE std::enable_if_t<(sizeof(T)<=sizeof(U)), INDEX_TYPE > convertSize(INDEX_TYPE const numU)
Convert a number of values of type U to a number of values of type T.
Definition: typeManipulation.hpp:476
Contains templates useful for type manipulation.
#define LVARRAY_ERROR_IF_NE_MSG(lhs, rhs, msg)
Raise a hard error if two values are not equal.
Definition: Macros.hpp:305
chai::ExecutionSpace toChaiExecutionSpace(MemorySpace const space)
Definition: ChaiBuffer.hpp:52
Implements the Buffer interface using CHAI.
Definition: ChaiBuffer.hpp:104
LVARRAY_HOST_DEVICE LVARRAY_INTEL_CONSTEXPR ChaiBuffer & operator=(ChaiBuffer const &src)
Copy assignment operator.
Definition: ChaiBuffer.hpp:247
LVARRAY_HOST_DEVICE constexpr std::enable_if_t< std::is_arithmetic< T >::value, T > min(T const a, T const b)
Definition: math.hpp:358
#define LVARRAY_LOG(...)
Print the expression.
Definition: Macros.hpp:77
T *LVARRAY_RESTRICT m_pointer
A pointer to the data.
Definition: ChaiBuffer.hpp:495
void move(MemorySpace const space, bool const touch) const
Move the buffer to the given execution space, optionally touching it.
Definition: ChaiBuffer.hpp:399
LVARRAY_HOST_DEVICE ChaiBuffer(ChaiBuffer const &src)
Copy constructor.
Definition: ChaiBuffer.hpp:183
MemorySpace toMemorySpace(chai::ExecutionSpace const space)
Definition: ChaiBuffer.hpp:72
chai::ArrayManager & getArrayManager()
Definition: ChaiBuffer.hpp:39
chai::PointerRecord * m_pointerRecord
A pointer to the chai PointerRecord, keeps track of the memory space information. ...
Definition: ChaiBuffer.hpp:501
Contains functions for manipulating buffers.
void reallocate(std::ptrdiff_t const size, MemorySpace const space, std::ptrdiff_t const newCapacity)
Reallocate the buffer to the new capacity.
Definition: ChaiBuffer.hpp:281
#define LVARRAY_ERROR(MSG)
Abort execution.
Definition: Macros.hpp:143
LVARRAY_HOST_DEVICE constexpr T & operator[](INDEX_TYPE const i) const
Definition: ChaiBuffer.hpp:360
DISABLE_HD_WARNING LVARRAY_HOST_DEVICE void free(BUFFER &buf, std::ptrdiff_t const size)
Destroy the values in the buffer and free it&#39;s memory.
Definition: bufferManipulation.hpp:188
camp::resources::Platform MemorySpace
an alias for camp::resources::Platform.
Definition: bufferManipulation.hpp:31
Contains functions for manipulating a contiguous array of values.
ChaiBuffer(bool)
Constructor for creating an empty Buffer.
Definition: ChaiBuffer.hpp:134
The top level namespace.
Definition: Array.hpp:24
LVARRAY_HOST_DEVICE constexpr std::ptrdiff_t capacity() const
Definition: ChaiBuffer.hpp:334
std::remove_const_t< T > T_non_const
An alias for the non const version of T.
Definition: ChaiBuffer.hpp:115
Contains a bunch of macro definitions.
LVARRAY_HOST_DEVICE constexpr ChaiBuffer()
Default constructor, creates an uninitialized ChaiBuffer.
Definition: ChaiBuffer.hpp:123
DISABLE_HD_WARNING LVARRAY_HOST_DEVICE void destroy(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size)
Destory the values in the array.
Definition: arrayManipulation.hpp:152
LVARRAY_HOST_DEVICE LVARRAY_INTEL_CONSTEXPR ChaiBuffer & operator=(ChaiBuffer &&src)
Move assignment operator.
Definition: ChaiBuffer.hpp:261
std::enable_if_t< bufferManipulation::HasMemberFunction_move< U > > moveInnerData(MemorySpace const space, std::ptrdiff_t const size, bool const touch) const
Move inner allocations to the memory space space.
Definition: ChaiBuffer.hpp:473
#define LVARRAY_ERROR_IF_NE(lhs, rhs)
Raise a hard error if two values are not equal.
Definition: Macros.hpp:321
LVARRAY_HOST_DEVICE constexpr ChaiBuffer(ChaiBuffer< U > const &src)
Create a shallow copy of src but with a different type.
Definition: ChaiBuffer.hpp:235
LVARRAY_HOST_DEVICE constexpr ChaiBuffer(ChaiBuffer &&src)
Move constructor.
Definition: ChaiBuffer.hpp:218
std::enable_if_t< !bufferManipulation::HasMemberFunction_move< U > > moveInnerData(MemorySpace const, std::ptrdiff_t const, bool const) const
Move inner allocations to the memory space space.
Definition: ChaiBuffer.hpp:491
LVARRAY_HOST_DEVICE constexpr T * data() const
Definition: ChaiBuffer.hpp:341
constexpr void registerTouch(MemorySpace const space) const
Touch the buffer in the given space.
Definition: ChaiBuffer.hpp:431
std::string calculateSize(size_t const bytes)
Definition: system.cpp:406
#define LVARRAY_HOST_DEVICE
Mark a function for both host and device usage.
Definition: Macros.hpp:549
LVARRAY_HOST_DEVICE constexpr chai::PointerRecord & pointerRecord() const
Return a reference to the associated CHAI PointerRecord.
Definition: ChaiBuffer.hpp:349