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 )
60  return chai::GPU;
61 #endif
62 #if defined(LVARRAY_USE_HIP)
63  if( space == MemorySpace::hip )
64  return chai::GPU;
65 #endif
66 
67  LVARRAY_ERROR( "Unrecognized memory space " << static_cast< int >( space ) );
68 
69  return chai::NONE;
70 }
71 
76 inline MemorySpace toMemorySpace( chai::ExecutionSpace const space )
77 {
78  if( space == chai::NONE )
79  return MemorySpace::undefined;
80  if( space == chai::CPU )
81  return MemorySpace::host;
82 #if defined(LVARRAY_USE_CUDA)
83  if( space == chai::GPU )
84  return MemorySpace::cuda;
85 #endif
86 #if defined(LVARRAY_USE_HIP)
87  if( space == chai::GPU )
88  return MemorySpace::hip;
89 #endif
90 
91  LVARRAY_ERROR( "Unrecognized execution space " << static_cast< int >( space ) );
92 
93  return MemorySpace::undefined;
94 }
95 
96 } // namespace internal
97 
111 template< typename T >
113 {
114 public:
115 
117  using value_type = T;
118 
120  constexpr static bool hasShallowCopy = true;
121 
123  using T_non_const = std::remove_const_t< T >;
124 
130  LVARRAY_HOST_DEVICE inline constexpr
132  m_pointer( nullptr ),
133  m_capacity( 0 ),
134  m_pointerRecord( nullptr )
135  {}
136 
144  ChaiBuffer( bool ):
145  m_pointer( nullptr ),
146  m_capacity( 0 )
147  #if !defined(LVARRAY_DEVICE_COMPILE)
148  , m_pointerRecord( new chai::PointerRecord{} )
149  #else
150  , m_pointerRecord( nullptr )
151  #endif
152  {
153  #if defined(LVARRAY_DEVICE_COMPILE)
154  LVARRAY_ERROR( "Creating a new ChaiBuffer on device is not supported. This is often the result of capturing an array on device instead of a view." );
155  #else
156  m_pointerRecord->m_size = 0;
157  setName( "" );
158 
159  for( int space = chai::CPU; space < chai::NUM_EXECUTION_SPACES; ++space )
160  {
161  m_pointerRecord->m_allocators[ space ] = internal::getArrayManager().getAllocatorId( chai::ExecutionSpace( space ) );
162  }
163  #endif
164  }
165 
174  ChaiBuffer( std::initializer_list< MemorySpace > const & spaces,
175  std::initializer_list< umpire::Allocator > const & allocators ):
176  m_pointer( nullptr ),
177  m_capacity( 0 )
178  #if !defined(LVARRAY_DEVICE_COMPILE)
179  , m_pointerRecord( new chai::PointerRecord{} )
180  #else
181  , m_pointerRecord( nullptr )
182  #endif
183  {
184  #if defined(LVARRAY_DEVICE_COMPILE)
185  LVARRAY_ERROR( "Creating a new ChaiBuffer on device is not supported." );
186  LVARRAY_UNUSED_VARIABLE( spaces );
187  LVARRAY_UNUSED_VARIABLE( allocators );
188  #else
189  m_pointerRecord->m_size = 0;
190  setName( "" );
191 
192  LVARRAY_ERROR_IF_NE( spaces.size(), allocators.size() );
193 
194  for( int space = chai::CPU; space < chai::NUM_EXECUTION_SPACES; ++space )
195  {
196  m_pointerRecord->m_allocators[ space ] = internal::getArrayManager().getAllocatorId( chai::ExecutionSpace( space ) );
197  }
198 
199  for( std::size_t i = 0; i < spaces.size(); ++i )
200  {
201  m_pointerRecord->m_allocators[ internal::toChaiExecutionSpace( spaces.begin()[ i ] ) ] = allocators.begin()[ i ].getId();
202  }
203  #endif
204  }
205 
212  LVARRAY_HOST_DEVICE inline
213  ChaiBuffer( ChaiBuffer const & src ):
214  m_pointer( src.m_pointer ),
215  m_capacity( src.m_capacity ),
216  m_pointerRecord( src.m_pointerRecord )
217  {
218  #if defined(LVARRAY_USE_DEVICE) && !defined(LVARRAY_DEVICE_COMPILE)
219  move( internal::toMemorySpace( internal::getArrayManager().getExecutionSpace() ), true );
220  #endif
221  }
222 
230  LVARRAY_HOST_DEVICE inline
231  ChaiBuffer( ChaiBuffer const & src, std::ptrdiff_t const size ):
232  m_pointer( src.m_pointer ),
233  m_capacity( src.m_capacity ),
234  m_pointerRecord( src.m_pointerRecord )
235  {
236  #if defined(LVARRAY_USE_DEVICE) && !defined(LVARRAY_DEVICE_COMPILE)
237  moveNested( internal::toMemorySpace( internal::getArrayManager().getExecutionSpace() ), size, true );
238  #else
239  LVARRAY_UNUSED_VARIABLE( size );
240  #endif
241  }
242 
247  LVARRAY_HOST_DEVICE inline constexpr
249  m_pointer( src.m_pointer ),
250  m_capacity( src.m_capacity ),
251  m_pointerRecord( src.m_pointerRecord )
252  {
253  src.m_capacity = 0;
254  src.m_pointer = nullptr;
255  src.m_pointerRecord = nullptr;
256  }
257 
263  template< typename U >
264  LVARRAY_HOST_DEVICE inline constexpr
265  ChaiBuffer( ChaiBuffer< U > const & src ):
266  m_pointer( reinterpret_cast< T * >( src.data() ) ),
267  m_capacity( typeManipulation::convertSize< T, U >( src.capacity() ) ),
268  m_pointerRecord( &src.pointerRecord() )
269  {}
270 
276  LVARRAY_HOST_DEVICE inline LVARRAY_INTEL_CONSTEXPR
277  ChaiBuffer & operator=( ChaiBuffer const & src )
278  {
279  m_capacity = src.m_capacity;
280  m_pointer = src.m_pointer;
281  m_pointerRecord = src.m_pointerRecord;
282  return *this;
283  }
284 
290  LVARRAY_HOST_DEVICE inline LVARRAY_INTEL_CONSTEXPR
292  {
293  m_capacity = src.m_capacity;
294  m_pointer = src.m_pointer;
295  m_pointerRecord = src.m_pointerRecord;
296 
297  src.m_capacity = 0;
298  src.m_pointer = nullptr;
299  src.m_pointerRecord = nullptr;
300 
301  return *this;
302  }
303 
313  void reallocate( std::ptrdiff_t const size,
314  MemorySpace const space,
315  std::ptrdiff_t const newCapacity )
316  {
317  #if defined(LVARRAY_DEVICE_COMPILE)
318  LVARRAY_ERROR( "Allocation from device is not supported." );
319  LVARRAY_UNUSED_VARIABLE( size );
320  LVARRAY_UNUSED_VARIABLE( space );
321  LVARRAY_UNUSED_VARIABLE( newCapacity );
322  #else
323  move( space, true );
324  chai::PointerRecord * const newRecord = new chai::PointerRecord{};
325  newRecord->m_size = newCapacity * sizeof( T );
326  newRecord->m_user_callback = m_pointerRecord->m_user_callback;
327 
328  for( int s = chai::CPU; s < chai::NUM_EXECUTION_SPACES; ++s )
329  {
330  newRecord->m_allocators[ s ] = m_pointerRecord->m_allocators[ s ];
331  }
332 
333  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
334 
335  internal::chaiLock.lock();
336  internal::getArrayManager().allocate( newRecord, chaiSpace );
337  internal::chaiLock.unlock();
338 
339  T * const newPointer = static_cast< T * >( newRecord->m_pointers[ chaiSpace ] );
340 
341  if( size > 0 )
342  {
343  LVARRAY_ERROR_IF_NE_MSG( space, MemorySpace::host, "Calling reallocate with a non-zero current size is not yet supporeted for the GPU." );
344  std::ptrdiff_t const overlapAmount = std::min( newCapacity, size );
345  arrayManipulation::uninitializedMove( newPointer, overlapAmount, m_pointer );
346  arrayManipulation::destroy( m_pointer, size );
347  }
348 
349  free();
350  m_capacity = newCapacity;
351  m_pointer = newPointer;
352  m_pointerRecord = newRecord;
353  registerTouch( space );
354  #endif
355  }
356 
362  LVARRAY_HOST_DEVICE inline
363  void free()
364  {
365  #if defined(LVARRAY_DEVICE_COMPILE)
366  LVARRAY_ERROR( "Deallocation from device is not supported." );
367  #else
368  std::lock_guard< std::mutex > lock( internal::chaiLock );
369  internal::getArrayManager().free( m_pointerRecord );
370  m_capacity = 0;
371  m_pointer = nullptr;
372  m_pointerRecord = nullptr;
373  #endif
374  }
375 
379  LVARRAY_HOST_DEVICE inline constexpr
380  std::ptrdiff_t capacity() const
381  { return m_capacity; }
382 
386  LVARRAY_HOST_DEVICE inline constexpr
387  T * data() const
388  { return m_pointer; }
389 
394  LVARRAY_HOST_DEVICE inline constexpr
395  chai::PointerRecord & pointerRecord() const
396  { return *m_pointerRecord; }
397 
404  template< typename INDEX_TYPE >
405  LVARRAY_HOST_DEVICE inline constexpr
406  T & operator[]( INDEX_TYPE const i ) const
407  { return m_pointer[ i ]; }
408 
416  inline
417  void moveNested( MemorySpace const space, std::ptrdiff_t const size, bool const touch ) const
418  {
419  #if defined(LVARRAY_USE_CUDA) || defined(LVARRAY_USE_HIP )
420  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
421  if( m_pointerRecord == nullptr ||
422  m_capacity == 0 ||
423  chaiSpace == chai::NONE ) return;
424 
425  chai::ExecutionSpace const prevSpace = m_pointerRecord->m_last_space;
426 
427  if( prevSpace == chai::CPU && prevSpace != chaiSpace ) moveInnerData( space, size, touch );
428 
429  move( space, touch );
430 
431  if( prevSpace == chai::GPU && prevSpace != chaiSpace ) moveInnerData( space, size, touch );
432  #else
433  LVARRAY_ERROR_IF_NE( space, MemorySpace::host );
434  LVARRAY_UNUSED_VARIABLE( size );
435  LVARRAY_UNUSED_VARIABLE( touch );
436  #endif
437  }
438 
445  void move( MemorySpace const space, bool const touch ) const
446  {
447  #if defined(LVARRAY_USE_CUDA) || defined(LVARRAY_USE_HIP)
448  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
449  if( m_pointerRecord == nullptr ||
450  m_capacity == 0 ||
451  chaiSpace == chai::NONE ) return;
452 
453  auto & am = internal::getArrayManager();
454  const_cast< T * & >( m_pointer ) =
455  static_cast< T * >( am.move( const_cast< T_non_const * >( m_pointer ), m_pointerRecord, chaiSpace ) );
456 
457  if( !std::is_const< T >::value && touch ) m_pointerRecord->m_touched[ chaiSpace ] = true;
458  m_pointerRecord->m_last_space = chaiSpace;
459  #else
460  LVARRAY_ERROR_IF_NE( space, MemorySpace::host );
461  LVARRAY_UNUSED_VARIABLE( touch );
462  #endif
463  }
464 
469  { return internal::toMemorySpace( m_pointerRecord->m_last_space ); }
470 
475  inline constexpr
476  void registerTouch( MemorySpace const space ) const
477  {
478  chai::ExecutionSpace const chaiSpace = internal::toChaiExecutionSpace( space );
479  m_pointerRecord->m_touched[ chaiSpace ] = true;
480  m_pointerRecord->m_last_space = chaiSpace;
481  }
482 
488  template< typename U=ChaiBuffer< T > >
489  void setName( std::string const & name )
490  {
491  std::string const typeString = system::demangleType< U >();
492  m_pointerRecord->m_user_callback =
493  [name, typeString]( chai::PointerRecord const * const record, chai::Action const act, chai::ExecutionSpace const s )
494  {
495  if( act == chai::ACTION_MOVE )
496  {
497  std::string const size = system::calculateSize( record->m_size );
498  std::string const paddedSize = std::string( 9 - size.size(), ' ' ) + size;
499  char const * const spaceStr = ( s == chai::CPU ) ? "HOST " : "DEVICE";
500  LVARRAY_LOG( "Moved " << paddedSize << " to the " << spaceStr << ": " << typeString << " " << name );
501  }
502  };
503  }
504 
505 private:
506 
516  template< typename U=T_non_const >
517  std::enable_if_t< bufferManipulation::HasMemberFunction_move< U > >
518  moveInnerData( MemorySpace const space, std::ptrdiff_t const size, bool const touch ) const
519  {
520  if( space == MemorySpace::undefined ) return;
521 
522  for( std::ptrdiff_t i = 0; i < size; ++i )
523  {
524  const_cast< T_non_const * >( m_pointer )[ i ].move( space, touch );
525  }
526  }
527 
534  template< typename U=T_non_const >
535  std::enable_if_t< !bufferManipulation::HasMemberFunction_move< U > >
536  moveInnerData( MemorySpace const, std::ptrdiff_t const, bool const ) const
537  {}
538 
540  T * LVARRAY_RESTRICT m_pointer = nullptr;
541 
543  std::ptrdiff_t m_capacity = 0;
544 
546  chai::PointerRecord * m_pointerRecord = nullptr;
547 };
548 
549 } /* namespace LvArray */
#define LVARRAY_UNUSED_VARIABLE(X)
Mark X as an unused variable, used to silence compiler warnings.
Definition: Macros.hpp:79
LVARRAY_HOST_DEVICE 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:313
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:489
MemorySpace getPreviousSpace() const
Definition: ChaiBuffer.hpp:468
T value_type
Alias for T used used in the bufferManipulation functions.
Definition: ChaiBuffer.hpp:117
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:417
LVARRAY_HOST_DEVICE void free()
Free the data in the buffer but does not destroy any values.
Definition: ChaiBuffer.hpp:363
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:231
std::ptrdiff_t m_capacity
The size of the allocation.
Definition: ChaiBuffer.hpp:543
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
LVARRAY_HOST_DEVICE LVARRAY_FORCE_INLINE constexpr std::enable_if_t< std::is_arithmetic< T >::value, T > min(T const a, T const b)
Definition: math.hpp:362
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:344
chai::ExecutionSpace toChaiExecutionSpace(MemorySpace const space)
Definition: ChaiBuffer.hpp:52
Implements the Buffer interface using CHAI.
Definition: ChaiBuffer.hpp:112
LVARRAY_HOST_DEVICE LVARRAY_INTEL_CONSTEXPR ChaiBuffer & operator=(ChaiBuffer const &src)
Copy assignment operator.
Definition: ChaiBuffer.hpp:277
#define LVARRAY_LOG(...)
Print the expression.
Definition: Macros.hpp:105
T *LVARRAY_RESTRICT m_pointer
A pointer to the data.
Definition: ChaiBuffer.hpp:540
void move(MemorySpace const space, bool const touch) const
Move the buffer to the given execution space, optionally touching it.
Definition: ChaiBuffer.hpp:445
LVARRAY_HOST_DEVICE ChaiBuffer(ChaiBuffer const &src)
Copy constructor.
Definition: ChaiBuffer.hpp:213
MemorySpace toMemorySpace(chai::ExecutionSpace const space)
Definition: ChaiBuffer.hpp:76
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:546
Contains functions for manipulating buffers.
#define LVARRAY_ERROR(MSG)
Abort execution.
Definition: Macros.hpp:176
LVARRAY_HOST_DEVICE constexpr T & operator[](INDEX_TYPE const i) const
Definition: ChaiBuffer.hpp:406
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.
LVARRAY_HOST_DEVICE 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:174
The top level namespace.
Definition: Array.hpp:24
LVARRAY_HOST_DEVICE constexpr std::ptrdiff_t capacity() const
Definition: ChaiBuffer.hpp:380
std::remove_const_t< T > T_non_const
An alias for the non const version of T.
Definition: ChaiBuffer.hpp:123
Contains a bunch of macro definitions.
LVARRAY_HOST_DEVICE constexpr ChaiBuffer()
Default constructor, creates an uninitialized ChaiBuffer.
Definition: ChaiBuffer.hpp:131
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:291
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:518
#define LVARRAY_ERROR_IF_NE(lhs, rhs)
Raise a hard error if two values are not equal.
Definition: Macros.hpp:360
LVARRAY_HOST_DEVICE constexpr ChaiBuffer(ChaiBuffer< U > const &src)
Create a shallow copy of src but with a different type.
Definition: ChaiBuffer.hpp:265
LVARRAY_HOST_DEVICE ChaiBuffer(bool)
Constructor for creating an empty Buffer.
Definition: ChaiBuffer.hpp:144
LVARRAY_HOST_DEVICE constexpr ChaiBuffer(ChaiBuffer &&src)
Move constructor.
Definition: ChaiBuffer.hpp:248
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:536
LVARRAY_HOST_DEVICE constexpr T * data() const
Definition: ChaiBuffer.hpp:387
constexpr void registerTouch(MemorySpace const space) const
Touch the buffer in the given space.
Definition: ChaiBuffer.hpp:476
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:600
LVARRAY_HOST_DEVICE constexpr chai::PointerRecord & pointerRecord() const
Return a reference to the associated CHAI PointerRecord.
Definition: ChaiBuffer.hpp:395