/* //@HEADER // ************************************************************************ // // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the Corporation nor the names of the // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) // // ************************************************************************ //@HEADER */ #ifndef KOKKOS_DYNAMIC_VIEW_HPP #define KOKKOS_DYNAMIC_VIEW_HPP #include #include #include namespace Kokkos { namespace Experimental { /** \brief Dynamic views are restricted to rank-one and no layout. * Subviews are not allowed. */ template< typename DataType , typename ... P > class DynamicView : public Kokkos::Experimental::ViewTraits< DataType , P ... > { public: typedef ViewTraits< DataType , P ... > traits ; private: template< class , class ... > friend class DynamicView ; typedef Kokkos::Experimental::Impl::SharedAllocationTracker track_type ; static_assert( traits::rank == 1 && traits::rank_dynamic == 1 , "DynamicView must be rank-one" ); static_assert( std::is_trivial< typename traits::value_type >::value && std::is_same< typename traits::specialize , void >::value , "DynamicView must have trivial data type" ); public: typedef Kokkos::Experimental::MemoryPool < typename traits::memory_space , typename traits::execution_space > memory_pool ; private: memory_pool m_pool ; track_type m_track ; typename traits::value_type ** m_chunks ; unsigned m_chunk_shift ; unsigned m_chunk_mask ; unsigned m_chunk_max ; public: //---------------------------------------------------------------------- /** \brief Compatible view of array of scalar types */ typedef DynamicView< typename traits::data_type , typename traits::device_type > array_type ; /** \brief Compatible view of const data type */ typedef DynamicView< typename traits::const_data_type , typename traits::device_type > const_type ; /** \brief Compatible view of non-const data type */ typedef DynamicView< typename traits::non_const_data_type , typename traits::device_type > non_const_type ; /** \brief Must be accessible everywhere */ typedef DynamicView HostMirror ; //---------------------------------------------------------------------- enum { Rank = 1 }; KOKKOS_INLINE_FUNCTION constexpr size_t size() const { return Kokkos::Impl::VerifyExecutionCanAccessMemorySpace < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::value ? // Runtime size is at the end of the chunk pointer array (*reinterpret_cast( m_chunks + m_chunk_max )) << m_chunk_shift : 0 ; } template< typename iType > KOKKOS_INLINE_FUNCTION constexpr size_t extent( const iType & r ) const { return r == 0 ? size() : 1 ; } template< typename iType > KOKKOS_INLINE_FUNCTION constexpr size_t extent_int( const iType & r ) const { return r == 0 ? size() : 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_0() const { return size(); } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_1() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_2() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_3() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_4() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_5() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_6() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_7() const { return 1 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_0() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_1() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_2() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_3() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_4() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_5() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_6() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr size_t stride_7() const { return 0 ; } template< typename iType > KOKKOS_INLINE_FUNCTION void stride( iType * const s ) const { *s = 0 ; } //---------------------------------------------------------------------- // Range span is the span which contains all members. typedef typename traits::value_type & reference_type ; typedef typename traits::value_type * pointer_type ; enum { reference_type_is_lvalue_reference = std::is_lvalue_reference< reference_type >::value }; KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { return false ; } KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return 0 ; } KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { return 0 ; } //---------------------------------------- template< typename I0 , class ... Args > KOKKOS_INLINE_FUNCTION reference_type operator()( const I0 & i0 , const Args & ... args ) const { static_assert( Kokkos::Impl::are_integral::value , "Indices must be integral type" ); Kokkos::Impl::VerifyExecutionCanAccessMemorySpace < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); // Which chunk is being indexed. const uintptr_t ic = uintptr_t( i0 >> m_chunk_shift ); typename traits::value_type * volatile * const ch = m_chunks + ic ; // Do bounds checking if enabled or if the chunk pointer is zero. // If not bounds checking then we assume a non-zero pointer is valid. #if ! defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) if ( 0 == *ch ) #endif { // Verify that allocation of the requested chunk in in progress. // The allocated chunk counter is m_chunks[ m_chunk_max ] const uintptr_t n = *reinterpret_cast( m_chunks + m_chunk_max ); if ( n <= ic ) { Kokkos::abort("Kokkos::DynamicView array bounds error"); } // Allocation of this chunk is in progress // so wait for allocation to complete. while ( 0 == *ch ); } return (*ch)[ i0 & m_chunk_mask ]; } //---------------------------------------- /** \brief Resizing in parallel only increases the array size, * never decrease. */ KOKKOS_INLINE_FUNCTION void resize_parallel( size_t n ) const { typedef typename traits::value_type value_type ; Kokkos::Impl::VerifyExecutionCanAccessMemorySpace < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); const uintptr_t NC = ( n + m_chunk_mask ) >> m_chunk_shift ; if ( m_chunk_max < NC ) { #if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) printf("DynamicView::resize_parallel(%lu) m_chunk_max(%lu) NC(%lu)\n" , n , m_chunk_max , NC ); #endif Kokkos::abort("DynamicView::resize_parallel exceeded maximum size"); } typename traits::value_type * volatile * const ch = m_chunks ; // The allocated chunk counter is m_chunks[ m_chunk_max ] uintptr_t volatile * const pc = reinterpret_cast( m_chunks + m_chunk_max ); // Potentially concurrent iteration of allocation to the required size. for ( uintptr_t jc = *pc ; jc < NC ; ) { // Claim the 'jc' chunk to-be-allocated index const uintptr_t jc_try = jc ; // Jump iteration to the chunk counter. jc = atomic_compare_exchange( pc , jc_try , jc_try + 1 ); if ( jc_try == jc ) { ch[jc_try] = reinterpret_cast( m_pool.allocate( sizeof(value_type) << m_chunk_shift )); Kokkos::memory_fence(); } } } /** \brief Resizing in serial can grow or shrink the array size, */ inline void resize_serial( size_t n ) { Kokkos::Impl::VerifyExecutionCanAccessMemorySpace < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); const uintptr_t NC = ( n + m_chunk_mask ) >> m_chunk_shift ; if ( m_chunk_max < NC ) { Kokkos::abort("DynamicView::resize_serial exceeded maximum size"); } uintptr_t * const pc = reinterpret_cast( m_chunks + m_chunk_max ); if ( *pc < NC ) { while ( *pc < NC ) { m_chunks[*pc] = m_pool.allocate( sizeof(traits::value_type) << m_chunk_shift ); ++*pc ; } } else { while ( NC + 1 <= *pc ) { --*pc ; m_pool.deallocate( m_chunks[*pc] , sizeof(traits::value_type) << m_chunk_shift ); m_chunks[*pc] = 0 ; } } } //---------------------------------------------------------------------- ~DynamicView() = default ; DynamicView() = default ; DynamicView( DynamicView && ) = default ; DynamicView( const DynamicView & ) = default ; DynamicView & operator = ( DynamicView && ) = default ; DynamicView & operator = ( const DynamicView & ) = default ; template< class RT , class ... RP > KOKKOS_INLINE_FUNCTION DynamicView( const DynamicView & rhs ) : m_pool( rhs.m_pool ) , m_track( rhs.m_track ) , m_chunks( rhs.m_chunks ) , m_chunk_shift( rhs.m_chunk_shift ) , m_chunk_mask( rhs.m_chunk_mask ) , m_chunk_max( rhs.m_chunk_max ) { } //---------------------------------------------------------------------- struct Destroy { memory_pool m_pool ; typename traits::value_type ** m_chunks ; unsigned m_chunk_max ; bool m_destroy ; // Initialize or destroy array of chunk pointers. // Two entries beyond the max chunks are allocation counters. KOKKOS_INLINE_FUNCTION void operator()( unsigned i ) const { if ( m_destroy && i < m_chunk_max && 0 != m_chunks[i] ) { m_pool.deallocate( m_chunks[i] , m_pool.get_min_chunk_size() ); } m_chunks[i] = 0 ; } void execute( bool arg_destroy ) { typedef Kokkos::RangePolicy< typename traits::execution_space > Range ; m_destroy = arg_destroy ; Kokkos::Impl::ParallelFor closure( *this , Range(0, m_chunk_max + 1) ); closure.execute(); traits::execution_space::fence(); } void construct_shared_allocation() { execute( false ); } void destroy_shared_allocation() { execute( true ); } Destroy() = default ; Destroy( Destroy && ) = default ; Destroy( const Destroy & ) = default ; Destroy & operator = ( Destroy && ) = default ; Destroy & operator = ( const Destroy & ) = default ; Destroy( const memory_pool & arg_pool , typename traits::value_type ** arg_chunk , const unsigned arg_chunk_max ) : m_pool( arg_pool ) , m_chunks( arg_chunk ) , m_chunk_max( arg_chunk_max ) , m_destroy( false ) {} }; /**\brief Allocation constructor * * Memory is allocated in chunks from the memory pool. * The chunk size conforms to the memory pool's chunk size. * A maximum size is required in order to allocate a * chunk-pointer array. */ explicit inline DynamicView( const std::string & arg_label , const memory_pool & arg_pool , const size_t arg_size_max ) : m_pool( arg_pool ) , m_track() , m_chunks(0) // The memory pool chunk is guaranteed to be a power of two , m_chunk_shift( Kokkos::Impl::integral_power_of_two( m_pool.get_min_chunk_size()/sizeof(typename traits::value_type)) ) , m_chunk_mask( ( 1 << m_chunk_shift ) - 1 ) , m_chunk_max( ( arg_size_max + m_chunk_mask ) >> m_chunk_shift ) { Kokkos::Impl::VerifyExecutionCanAccessMemorySpace < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); // A functor to deallocate all of the chunks upon final destruction typedef typename traits::memory_space memory_space ; typedef Kokkos::Experimental::Impl::SharedAllocationRecord< memory_space , Destroy > record_type ; // Allocate chunk pointers and allocation counter record_type * const record = record_type::allocate( memory_space() , arg_label , ( sizeof(pointer_type) * ( m_chunk_max + 1 ) ) ); m_chunks = reinterpret_cast( record->data() ); record->m_destroy = Destroy( m_pool , m_chunks , m_chunk_max ); // Initialize to zero record->m_destroy.construct_shared_allocation(); m_track.assign_allocated_record_to_uninitialized( record ); } }; } // namespace Experimental } // namespace Kokkos namespace Kokkos { namespace Experimental { template< class T , class ... P > inline typename Kokkos::Experimental::DynamicView::HostMirror create_mirror_view( const Kokkos::Experimental::DynamicView & src ) { return src ; } template< class T , class ... DP , class ... SP > inline void deep_copy( const View & dst , const DynamicView & src ) { typedef View dst_type ; typedef DynamicView src_type ; typedef typename ViewTraits::execution_space dst_execution_space ; typedef typename ViewTraits::memory_space src_memory_space ; enum { DstExecCanAccessSrc = Kokkos::Impl::VerifyExecutionCanAccessMemorySpace< typename dst_execution_space::memory_space , src_memory_space >::value }; if ( DstExecCanAccessSrc ) { // Copying data between views in accessible memory spaces and either non-contiguous or incompatible shape. Kokkos::Experimental::Impl::ViewRemap< dst_type , src_type >( dst , src ); } else { Kokkos::Impl::throw_runtime_exception("deep_copy given views that would require a temporary allocation"); } } template< class T , class ... DP , class ... SP > inline void deep_copy( const DynamicView & dst , const View & src ) { typedef DynamicView dst_type ; typedef View src_type ; typedef typename ViewTraits::execution_space dst_execution_space ; typedef typename ViewTraits::memory_space src_memory_space ; enum { DstExecCanAccessSrc = Kokkos::Impl::VerifyExecutionCanAccessMemorySpace< typename dst_execution_space::memory_space , src_memory_space >::value }; if ( DstExecCanAccessSrc ) { // Copying data between views in accessible memory spaces and either non-contiguous or incompatible shape. Kokkos::Experimental::Impl::ViewRemap< dst_type , src_type >( dst , src ); } else { Kokkos::Impl::throw_runtime_exception("deep_copy given views that would require a temporary allocation"); } } } // namespace Experimental } // namespace Kokkos #endif /* #ifndef KOKKOS_DYNAMIC_VIEW_HPP */