Kokkos Core Kernels Package  Version of the Day
Kokkos_DynamicView.hpp
1 /*
2 //@HEADER
3 // ************************************************************************
4 //
5 // Kokkos v. 2.0
6 // Copyright (2014) Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov)
39 //
40 // ************************************************************************
41 //@HEADER
42 */
43 
44 #ifndef KOKKOS_DYNAMIC_VIEW_HPP
45 #define KOKKOS_DYNAMIC_VIEW_HPP
46 
47 #include <cstdio>
48 
49 #include <Kokkos_Core.hpp>
50 #include <impl/Kokkos_Error.hpp>
51 
52 namespace Kokkos {
53 namespace Experimental {
54 
58 template< typename DataType , typename ... P >
59 class DynamicView : public Kokkos::ViewTraits< DataType , P ... >
60 {
61 public:
62 
63  typedef Kokkos::ViewTraits< DataType , P ... > traits ;
64 
65 private:
66 
67  template< class , class ... > friend class DynamicView ;
68 
69  typedef Kokkos::Experimental::Impl::SharedAllocationTracker track_type ;
70 
71  static_assert( traits::rank == 1 && traits::rank_dynamic == 1
72  , "DynamicView must be rank-one" );
73 
74  static_assert( std::is_trivial< typename traits::value_type >::value &&
75  std::is_same< typename traits::specialize , void >::value
76  , "DynamicView must have trivial data type" );
77 
78 
79  template< class Space , bool = Kokkos::Impl::MemorySpaceAccess< Space , typename traits::memory_space >::accessible > struct verify_space
80  { KOKKOS_FORCEINLINE_FUNCTION static void check() {} };
81 
82  template< class Space > struct verify_space<Space,false>
83  { KOKKOS_FORCEINLINE_FUNCTION static void check()
84  { Kokkos::abort("Kokkos::DynamicView ERROR: attempt to access inaccessible memory space"); };
85  };
86 
87 public:
88 
89  typedef Kokkos::MemoryPool< typename traits::device_type > memory_pool ;
90 
91 private:
92 
93  memory_pool m_pool ;
94  track_type m_track ;
95  typename traits::value_type ** m_chunks ;
96  unsigned m_chunk_shift ;
97  unsigned m_chunk_mask ;
98  unsigned m_chunk_max ;
99 
100 public:
101 
102  //----------------------------------------------------------------------
103 
105  typedef DynamicView< typename traits::data_type ,
106  typename traits::device_type >
108 
110  typedef DynamicView< typename traits::const_data_type ,
111  typename traits::device_type >
113 
115  typedef DynamicView< typename traits::non_const_data_type ,
116  typename traits::device_type >
118 
121 
122  //----------------------------------------------------------------------
123 
124  enum { Rank = 1 };
125 
126  KOKKOS_INLINE_FUNCTION
127  size_t size() const noexcept
128  {
129  uintptr_t n = 0 ;
130 
132  < Kokkos::Impl::ActiveExecutionMemorySpace
133  , typename traits::memory_space
134  >::accessible ) {
135  n = *reinterpret_cast<const uintptr_t*>( m_chunks + m_chunk_max );
136  }
137 #if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST )
138  else {
139  Kokkos::Impl::DeepCopy< Kokkos::HostSpace
140  , typename traits::memory_space
141  , Kokkos::HostSpace::execution_space >
142  ( & n
143  , reinterpret_cast<const uintptr_t*>( m_chunks + m_chunk_max )
144  , sizeof(uintptr_t) );
145  }
146 #endif
147  return n << m_chunk_shift ;
148  }
149 
150  template< typename iType >
151  KOKKOS_INLINE_FUNCTION
152  size_t extent( const iType & r ) const
153  { return r == 0 ? size() : 1 ; }
154 
155  template< typename iType >
156  KOKKOS_INLINE_FUNCTION
157  size_t extent_int( const iType & r ) const
158  { return r == 0 ? size() : 1 ; }
159 
160  KOKKOS_INLINE_FUNCTION size_t dimension_0() const { return size(); }
161  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_1() const { return 1 ; }
162  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_2() const { return 1 ; }
163  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_3() const { return 1 ; }
164  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_4() const { return 1 ; }
165  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_5() const { return 1 ; }
166  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_6() const { return 1 ; }
167  KOKKOS_INLINE_FUNCTION constexpr size_t dimension_7() const { return 1 ; }
168 
169  KOKKOS_INLINE_FUNCTION constexpr size_t stride_0() const { return 0 ; }
170  KOKKOS_INLINE_FUNCTION constexpr size_t stride_1() const { return 0 ; }
171  KOKKOS_INLINE_FUNCTION constexpr size_t stride_2() const { return 0 ; }
172  KOKKOS_INLINE_FUNCTION constexpr size_t stride_3() const { return 0 ; }
173  KOKKOS_INLINE_FUNCTION constexpr size_t stride_4() const { return 0 ; }
174  KOKKOS_INLINE_FUNCTION constexpr size_t stride_5() const { return 0 ; }
175  KOKKOS_INLINE_FUNCTION constexpr size_t stride_6() const { return 0 ; }
176  KOKKOS_INLINE_FUNCTION constexpr size_t stride_7() const { return 0 ; }
177 
178  template< typename iType >
179  KOKKOS_INLINE_FUNCTION void stride( iType * const s ) const { *s = 0 ; }
180 
181  //----------------------------------------------------------------------
182  // Range span is the span which contains all members.
183 
184  typedef typename traits::value_type & reference_type ;
185  typedef typename traits::value_type * pointer_type ;
186 
187  enum { reference_type_is_lvalue_reference = std::is_lvalue_reference< reference_type >::value };
188 
189  KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { return false ; }
190  KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return 0 ; }
191  KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { return 0 ; }
192 
193  //----------------------------------------
194 
195  template< typename I0 , class ... Args >
196  KOKKOS_INLINE_FUNCTION
197  reference_type operator()( const I0 & i0 , const Args & ... args ) const
198  {
199  static_assert( Kokkos::Impl::are_integral<I0,Args...>::value
200  , "Indices must be integral type" );
201 
202  DynamicView::template verify_space< Kokkos::Impl::ActiveExecutionMemorySpace >::check();
203 
204  // Which chunk is being indexed.
205  const uintptr_t ic = uintptr_t( i0 >> m_chunk_shift );
206 
207  typename traits::value_type * volatile * const ch = m_chunks + ic ;
208 
209  // Do bounds checking if enabled or if the chunk pointer is zero.
210  // If not bounds checking then we assume a non-zero pointer is valid.
211 
212 #if ! defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK )
213  if ( 0 == *ch )
214 #endif
215  {
216  // Verify that allocation of the requested chunk in in progress.
217 
218  // The allocated chunk counter is m_chunks[ m_chunk_max ]
219  const uintptr_t n =
220  *reinterpret_cast<uintptr_t volatile *>( m_chunks + m_chunk_max );
221 
222  if ( n <= ic ) {
223  Kokkos::abort("Kokkos::DynamicView array bounds error");
224  }
225 
226  // Allocation of this chunk is in progress
227  // so wait for allocation to complete.
228  while ( 0 == *ch );
229  }
230 
231  return (*ch)[ i0 & m_chunk_mask ];
232  }
233 
234  //----------------------------------------
238  KOKKOS_INLINE_FUNCTION
239  void resize_parallel( size_t n ) const
240  {
241  typedef typename traits::value_type value_type ;
242 
243  DynamicView::template verify_space< Kokkos::Impl::ActiveExecutionMemorySpace >::check();
244 
245  const uintptr_t NC = ( n + m_chunk_mask ) >> m_chunk_shift ;
246 
247  if ( m_chunk_max < NC ) {
248 #if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK )
249  printf("DynamicView::resize_parallel(%lu) m_chunk_max(%u) NC(%lu)\n"
250  , n , m_chunk_max , NC );
251 #endif
252  Kokkos::abort("DynamicView::resize_parallel exceeded maximum size");
253  }
254 
255  typename traits::value_type * volatile * const ch = m_chunks ;
256 
257  // The allocated chunk counter is m_chunks[ m_chunk_max ]
258  uintptr_t volatile * const pc =
259  reinterpret_cast<uintptr_t volatile*>( m_chunks + m_chunk_max );
260 
261  // Potentially concurrent iteration of allocation to the required size.
262 
263  for ( uintptr_t jc = *pc ; jc < NC ; ) {
264 
265  // Claim the 'jc' chunk to-be-allocated index
266 
267  const uintptr_t jc_try = jc ;
268 
269  // Jump iteration to the chunk counter.
270 
271  jc = atomic_compare_exchange( pc , jc_try , jc_try + 1 );
272 
273  if ( jc_try == jc ) {
274 
275  ch[jc_try] = reinterpret_cast<value_type*>(
276  m_pool.allocate( sizeof(value_type) << m_chunk_shift ));
277 
278  if ( 0 == ch[jc_try] ) {
279  Kokkos::abort("DynamicView::resize_parallel exhausted memory pool");
280  }
281 
282  Kokkos::memory_fence();
283  }
284  }
285  }
286 
288  template< typename IntType >
289  inline
290  typename std::enable_if
291  < std::is_integral<IntType>::value &&
293  , typename traits::memory_space
294  >::accessible
295  >::type
296  resize_serial( IntType const & n )
297  {
298  typedef typename traits::value_type value_type ;
299  typedef value_type * pointer_type ;
300 
301  const uintptr_t NC = ( n + m_chunk_mask ) >> m_chunk_shift ;
302 
303  if ( m_chunk_max < NC ) {
304  Kokkos::abort("DynamicView::resize_serial exceeded maximum size");
305  }
306 
307  uintptr_t * const pc =
308  reinterpret_cast<uintptr_t*>( m_chunks + m_chunk_max );
309 
310  if ( *pc < NC ) {
311  while ( *pc < NC ) {
312  m_chunks[*pc] = reinterpret_cast<pointer_type>
313  ( m_pool.allocate( sizeof(value_type) << m_chunk_shift ) );
314  ++*pc ;
315  }
316  }
317  else {
318  while ( NC + 1 <= *pc ) {
319  --*pc ;
320  m_pool.deallocate( m_chunks[*pc]
321  , sizeof(value_type) << m_chunk_shift );
322  m_chunks[*pc] = 0 ;
323  }
324  }
325  }
326 
327  //----------------------------------------
328 
329  struct ResizeSerial {
330  memory_pool m_pool ;
331  typename traits::value_type ** m_chunks ;
332  uintptr_t * m_pc ;
333  uintptr_t m_nc ;
334  unsigned m_chunk_shift ;
335 
336  KOKKOS_INLINE_FUNCTION
337  void operator()( int ) const
338  {
339  typedef typename traits::value_type value_type ;
340  typedef value_type * pointer_type ;
341 
342  if ( *m_pc < m_nc ) {
343  while ( *m_pc < m_nc ) {
344  m_chunks[*m_pc] = reinterpret_cast<pointer_type>
345  ( m_pool.allocate( sizeof(value_type) << m_chunk_shift ) );
346  ++*m_pc ;
347  }
348  }
349  else {
350  while ( m_nc + 1 <= *m_pc ) {
351  --*m_pc ;
352  m_pool.deallocate( m_chunks[*m_pc]
353  , sizeof(value_type) << m_chunk_shift );
354  m_chunks[*m_pc] = 0 ;
355  }
356  }
357  }
358 
359  ResizeSerial( memory_pool const & arg_pool
360  , typename traits::value_type ** arg_chunks
361  , uintptr_t * arg_pc
362  , uintptr_t arg_nc
363  , unsigned arg_chunk_shift
364  )
365  : m_pool( arg_pool )
366  , m_chunks( arg_chunks )
367  , m_pc( arg_pc )
368  , m_nc( arg_nc )
369  , m_chunk_shift( arg_chunk_shift )
370  {}
371  };
372 
373  template< typename IntType >
374  inline
375  typename std::enable_if
376  < std::is_integral<IntType>::value &&
377  ! Kokkos::Impl::MemorySpaceAccess< Kokkos::HostSpace
378  , typename traits::memory_space
379  >::accessible
380  >::type
381  resize_serial( IntType const & n )
382  {
383  const uintptr_t NC = ( n + m_chunk_mask ) >> m_chunk_shift ;
384 
385  if ( m_chunk_max < NC ) {
386  Kokkos::abort("DynamicView::resize_serial exceeded maximum size");
387  }
388 
389  // Must dispatch kernel
390 
392 
393  uintptr_t * const pc =
394  reinterpret_cast<uintptr_t*>( m_chunks + m_chunk_max );
395 
397  closure( ResizeSerial( m_pool, m_chunks, pc, NC, m_chunk_shift )
398  , Range(0,1) );
399 
400  closure.execute();
401 
402  traits::execution_space::fence();
403  }
404 
405  //----------------------------------------------------------------------
406 
407  ~DynamicView() = default ;
408  DynamicView() = default ;
409  DynamicView( DynamicView && ) = default ;
410  DynamicView( const DynamicView & ) = default ;
411  DynamicView & operator = ( DynamicView && ) = default ;
412  DynamicView & operator = ( const DynamicView & ) = default ;
413 
414  template< class RT , class ... RP >
415  DynamicView( const DynamicView<RT,RP...> & rhs )
416  : m_pool( rhs.m_pool )
417  , m_track( rhs.m_track )
418  , m_chunks( (typename traits::value_type **) rhs.m_chunks )
419  , m_chunk_shift( rhs.m_chunk_shift )
420  , m_chunk_mask( rhs.m_chunk_mask )
421  , m_chunk_max( rhs.m_chunk_max )
422  {
423  typedef typename DynamicView<RT,RP...>::traits SrcTraits ;
424  typedef Kokkos::Impl::ViewMapping< traits , SrcTraits , void > Mapping ;
425  static_assert( Mapping::is_assignable , "Incompatible DynamicView copy construction" );
426  }
427 
428  //----------------------------------------------------------------------
429 
430  struct Destroy {
431  memory_pool m_pool ;
432  typename traits::value_type ** m_chunks ;
433  unsigned m_chunk_max ;
434  bool m_destroy ;
435 
436  // Initialize or destroy array of chunk pointers.
437  // Two entries beyond the max chunks are allocation counters.
438 
439  KOKKOS_INLINE_FUNCTION
440  void operator()( unsigned i ) const
441  {
442  if ( m_destroy && i < m_chunk_max && 0 != m_chunks[i] ) {
443  m_pool.deallocate( m_chunks[i] , m_pool.min_block_size() );
444  }
445  m_chunks[i] = 0 ;
446  }
447 
448  void execute( bool arg_destroy )
449  {
451 
452  m_destroy = arg_destroy ;
453 
455  closure( *this , Range(0, m_chunk_max + 1) );
456 
457  closure.execute();
458 
459  traits::execution_space::fence();
460  }
461 
462  void construct_shared_allocation()
463  { execute( false ); }
464 
465  void destroy_shared_allocation()
466  { execute( true ); }
467 
468  Destroy() = default ;
469  Destroy( Destroy && ) = default ;
470  Destroy( const Destroy & ) = default ;
471  Destroy & operator = ( Destroy && ) = default ;
472  Destroy & operator = ( const Destroy & ) = default ;
473 
474  Destroy( const memory_pool & arg_pool
475  , typename traits::value_type ** arg_chunk
476  , const unsigned arg_chunk_max )
477  : m_pool( arg_pool )
478  , m_chunks( arg_chunk )
479  , m_chunk_max( arg_chunk_max )
480  , m_destroy( false )
481  {}
482  };
483 
484 
492  explicit inline
493  DynamicView( const std::string & arg_label
494  , const memory_pool & arg_pool
495  , const size_t arg_size_max )
496  : m_pool( arg_pool )
497  , m_track()
498  , m_chunks(0)
499  // The memory pool chunk is guaranteed to be a power of two
500  , m_chunk_shift(
501  Kokkos::Impl::integral_power_of_two(
502  m_pool.min_block_size()/sizeof(typename traits::value_type)) )
503  , m_chunk_mask( ( 1 << m_chunk_shift ) - 1 )
504  , m_chunk_max( ( arg_size_max + m_chunk_mask ) >> m_chunk_shift )
505  {
506  // A functor to deallocate all of the chunks upon final destruction
507 
508  typedef typename traits::memory_space memory_space ;
509  typedef Kokkos::Experimental::Impl::SharedAllocationRecord< memory_space , Destroy > record_type ;
510 
511  // Allocate chunk pointers and allocation counter
512  record_type * const record =
513  record_type::allocate( memory_space()
514  , arg_label
515  , ( sizeof(pointer_type) * ( m_chunk_max + 1 ) ) );
516 
517  m_chunks = reinterpret_cast<pointer_type*>( record->data() );
518 
519  record->m_destroy = Destroy( m_pool , m_chunks , m_chunk_max );
520 
521  // Initialize to zero
522 
523  record->m_destroy.construct_shared_allocation();
524 
525  m_track.assign_allocated_record_to_uninitialized( record );
526  }
527 };
528 
529 } // namespace Experimental
530 } // namespace Kokkos
531 
532 namespace Kokkos {
533 namespace Experimental {
534 
535 template< class T , class ... P >
536 inline
538 create_mirror_view( const Kokkos::Experimental::DynamicView<T,P...> & src )
539 {
540  return src ;
541 }
542 
543 template< class T , class ... DP , class ... SP >
544 inline
545 void deep_copy( const View<T,DP...> & dst
546  , const DynamicView<T,SP...> & src
547  )
548 {
549  typedef View<T,DP...> dst_type ;
550  typedef DynamicView<T,SP...> src_type ;
551 
552  typedef typename ViewTraits<T,DP...>::execution_space dst_execution_space ;
553  typedef typename ViewTraits<T,SP...>::memory_space src_memory_space ;
554 
555  enum { DstExecCanAccessSrc =
557 
558  if ( DstExecCanAccessSrc ) {
559  // Copying data between views in accessible memory spaces and either non-contiguous or incompatible shape.
560  Kokkos::Experimental::Impl::ViewRemap< dst_type , src_type >( dst , src );
561  }
562  else {
563  Kokkos::Impl::throw_runtime_exception("deep_copy given views that would require a temporary allocation");
564  }
565 }
566 
567 template< class T , class ... DP , class ... SP >
568 inline
569 void deep_copy( const DynamicView<T,DP...> & dst
570  , const View<T,SP...> & src
571  )
572 {
573  typedef DynamicView<T,SP...> dst_type ;
574  typedef View<T,DP...> src_type ;
575 
576  typedef typename ViewTraits<T,DP...>::execution_space dst_execution_space ;
577  typedef typename ViewTraits<T,SP...>::memory_space src_memory_space ;
578 
579  enum { DstExecCanAccessSrc =
581 
582  if ( DstExecCanAccessSrc ) {
583  // Copying data between views in accessible memory spaces and either non-contiguous or incompatible shape.
584  Kokkos::Experimental::Impl::ViewRemap< dst_type , src_type >( dst , src );
585  }
586  else {
587  Kokkos::Impl::throw_runtime_exception("deep_copy given views that would require a temporary allocation");
588  }
589 }
590 
591 } // namespace Experimental
592 } // namespace Kokkos
593 
594 #endif /* #ifndef KOKKOS_DYNAMIC_VIEW_HPP */
595 
DynamicView< typename traits::data_type, typename traits::device_type > array_type
Compatible view of array of scalar types.
Dynamic views are restricted to rank-one and no layout. Subviews are not allowed. ...
DynamicView(const std::string &arg_label, const memory_pool &arg_pool, const size_t arg_size_max)
Allocation constructor.
std::enable_if< std::is_integral< IntType >::value &&Kokkos::Impl::MemorySpaceAccess< Kokkos::HostSpace, typename traits::memory_space >::accessible >::type resize_serial(IntType const &n)
Resizing in serial can grow or shrink the array size,.
DynamicView< typename traits::const_data_type, typename traits::device_type > const_type
Compatible view of const data type.
KOKKOS_INLINE_FUNCTION void resize_parallel(size_t n) const
Resizing in parallel only increases the array size, never decrease.
View to an array of data.
Can AccessSpace access MemorySpace ?
Memory space for main process and CPU execution spaces.
Memory management for host memory.
DynamicView HostMirror
Must be accessible everywhere.
Implementation of the ParallelFor operator that has a partial specialization for the device...
void deep_copy(const View< DT, DP... > &dst, typename ViewTraits< DT, DP... >::const_value_type &value, typename std::enable_if< std::is_same< typename ViewTraits< DT, DP... >::specialize, void >::value >::type *=0)
Deep copy a value from Host memory into a view.
Execution policy for work over a range of an integral type.
Traits class for accessing attributes of a View.
Access relationship between DstMemorySpace and SrcMemorySpace.
DynamicView< typename traits::non_const_data_type, typename traits::device_type > non_const_type
Compatible view of non-const data type.