diff --git a/examples/exampleArrayOfSets.cpp b/examples/exampleArrayOfSets.cpp index 64206df1..cf8309cc 100644 --- a/examples/exampleArrayOfSets.cpp +++ b/examples/exampleArrayOfSets.cpp @@ -59,8 +59,8 @@ TEST( ArrayOfSets, assimilate ) arrayOfArrays.emplaceBack( 2, 1 ); // Assimilate arrayOfArrays into arrayOfSets. - arrayOfSets.assimilate( std::move( arrayOfArrays ), - LvArray::sortedArrayManipulation::Description::SORTED_UNIQUE ); + arrayOfSets.assimilate< RAJA::loop_exec >( std::move( arrayOfArrays ), + LvArray::sortedArrayManipulation::Description::SORTED_UNIQUE ); // After being assimilated arrayOfArrays is empty. EXPECT_EQ( arrayOfArrays.size(), 0 ); @@ -84,8 +84,8 @@ TEST( ArrayOfSets, assimilate ) arrayOfArrays.emplaceBack( 1, 4 ); // Assimilate the arrayOfArrays yet again. - arrayOfSets.assimilate( std::move( arrayOfArrays ), - LvArray::sortedArrayManipulation::Description::UNSORTED_WITH_DUPLICATES ); + arrayOfSets.assimilate< RAJA::loop_exec >( std::move( arrayOfArrays ), + LvArray::sortedArrayManipulation::Description::UNSORTED_WITH_DUPLICATES ); EXPECT_EQ( arrayOfSets.size(), 2 ); EXPECT_EQ( arrayOfSets.sizeOfSet( 0 ), 2 ); diff --git a/src/ArrayOfArrays.hpp b/src/ArrayOfArrays.hpp index 1ce2231e..61facade 100644 --- a/src/ArrayOfArrays.hpp +++ b/src/ArrayOfArrays.hpp @@ -201,6 +201,7 @@ class ArrayOfArrays : protected ArrayOfArraysView< T, INDEX_TYPE, false, BUFFER_ } using ParentClass::resizeFromCapacities; + using ParentClass::resizeFromOffsets; ///@} diff --git a/src/ArrayOfArraysView.hpp b/src/ArrayOfArraysView.hpp index 7aebc595..f4a2102f 100644 --- a/src/ArrayOfArraysView.hpp +++ b/src/ArrayOfArraysView.hpp @@ -15,9 +15,11 @@ // Source includes #include "bufferManipulation.hpp" #include "arrayManipulation.hpp" +#include "sortedArrayManipulation.hpp" #include "ArraySlice.hpp" #include "typeManipulation.hpp" #include "math.hpp" +#include "umpireInterface.hpp" // TPL includes #include @@ -685,6 +687,27 @@ class ArrayOfArraysView m_offsets[ m_numArrays ] = m_offsets[ m_numArrays - 1 ] + sizeOfArray( m_numArrays - 1 ); } + /** + * @brief Clears the array and creates a new array with the given number of sub-arrays. + * @param numSubArrays The new number of arrays. + * @param offsets A pointer to an array of length @p numSubArrays+1 containing the offset + * of each new sub array. Offsets are precomputed by the caller. + * @param buffers A variadic pack of buffers to treat similarly to m_values. + */ + template< typename ... BUFFERS > + void resizeFromOffsets( INDEX_TYPE const numSubArrays, + INDEX_TYPE const * const offsets, + BUFFERS & ... buffers ) + { + auto const fillOffsets = [&]() + { + arrayManipulation::uninitializedCopy( offsets, + offsets + numSubArrays + 1, + m_offsets.data() ); + }; + resizeFromOffsetsImpl( numSubArrays, fillOffsets, buffers ... ); + } + /** * @tparam POLICY The RAJA policy used to convert @p capacities into the offsets array. * Should NOT be a device policy. @@ -699,39 +722,14 @@ class ArrayOfArraysView INDEX_TYPE const * const capacities, BUFFERS & ... buffers ) { - LVARRAY_ASSERT( arrayManipulation::isPositive( numSubArrays ) ); - - #ifdef LVARRAY_BOUNDS_CHECK - for( INDEX_TYPE i = 0; i < numSubArrays; ++i ) + auto const fillOffsets = [&]() { - LVARRAY_ERROR_IF_LT( capacities[ i ], 0 ); - } - #endif - - destroyValues( 0, m_numArrays, buffers ... ); - - bufferManipulation::reserve( m_sizes, m_numArrays, MemorySpace::host, numSubArrays ); - std::fill_n( m_sizes.data(), numSubArrays, 0 ); - - INDEX_TYPE const offsetsSize = ( m_numArrays == 0 ) ? 0 : m_numArrays + 1; - bufferManipulation::reserve( m_offsets, offsetsSize, MemorySpace::host, numSubArrays + 1 ); - - m_offsets[ 0 ] = 0; - // RAJA::inclusive_scan fails on empty input range - if( numSubArrays > 0 ) - { - // const_cast needed until for RAJA bug. - RAJA::inclusive_scan< POLICY >( const_cast< INDEX_TYPE * >( capacities ), - const_cast< INDEX_TYPE * >( capacities + numSubArrays ), + m_offsets[ 0 ] = 0; + RAJA::inclusive_scan< POLICY >( capacities, + capacities + numSubArrays, m_offsets.data() + 1 ); - } - - m_numArrays = numSubArrays; - INDEX_TYPE const maxOffset = m_offsets[ m_numArrays ]; - typeManipulation::forEachArg( [ maxOffset] ( auto & buffer ) - { - bufferManipulation::reserve( buffer, 0, MemorySpace::host, maxOffset ); - }, m_values, buffers ... ); + }; + resizeFromOffsetsImpl( numSubArrays, fillOffsets, buffers ... ); } ///@} @@ -1008,6 +1006,40 @@ class ArrayOfArraysView } }, m_values, buffers ... ); } + + /** + * @brief Clears the array and creates a new array with the given number of sub-arrays. + * @param numSubArrays The new number of arrays. + * @param fillOffsets A function that will be called to populate sub-array offsets. + * @param buffers A variadic pack of buffers to treat similarly to m_values. + * @note This is to be called by other resizeFrom functions. + */ + template< typename FUNC, typename ... BUFFERS > + void resizeFromOffsetsImpl( INDEX_TYPE const numSubArrays, + FUNC && fillOffsets, + BUFFERS & ... buffers ) + { + LVARRAY_ASSERT( arrayManipulation::isPositive( numSubArrays ) ); + + destroyValues( 0, m_numArrays, buffers ... ); + + bufferManipulation::reserve( m_sizes, m_numArrays, MemorySpace::host, numSubArrays ); + umpireInterface::memset( m_sizes.data(), 0, m_sizes.capacity() * sizeof( INDEX_TYPE ) ); + + INDEX_TYPE const offsetsSize = ( m_numArrays == 0 ) ? 0 : m_numArrays + 1; + bufferManipulation::reserve( m_offsets, offsetsSize, MemorySpace::host, numSubArrays + 1 ); + + fillOffsets(); + LVARRAY_ASSERT_EQ( m_offsets[0], 0 ); + LVARRAY_ASSERT( sortedArrayManipulation::isSorted( m_offsets.data(), m_offsets.data() + numSubArrays + 1 ) ); + + m_numArrays = numSubArrays; + INDEX_TYPE const maxOffset = m_offsets[ m_numArrays ]; + typeManipulation::forEachArg( [ maxOffset ] ( auto & buffer ) + { + bufferManipulation::reserve( buffer, 0, MemorySpace::host, maxOffset ); + }, m_values, buffers ... ); + } }; } /* namespace LvArray */ diff --git a/src/ArrayOfSets.hpp b/src/ArrayOfSets.hpp index c8b75190..431b37cf 100644 --- a/src/ArrayOfSets.hpp +++ b/src/ArrayOfSets.hpp @@ -121,10 +121,11 @@ class ArrayOfSets : protected ArrayOfSetsView< T, INDEX_TYPE, BUFFER_TYPE > /** * @brief Steal the resources from an ArrayOfArrays and convert it to an ArrayOfSets. + * @tparam POLICY a RAJA execution policy to use when sorting/removing duplicates in sub-arrays. * @param src the ArrayOfArrays to convert. * @param desc describes the type of data in the source. - * TODO: Add a RAJA policy template parameter. */ + template< typename POLICY > inline void assimilate( ArrayOfArrays< T, INDEX_TYPE, BUFFER_TYPE > && src, sortedArrayManipulation::Description const desc ) @@ -133,38 +134,43 @@ class ArrayOfSets : protected ArrayOfSetsView< T, INDEX_TYPE, BUFFER_TYPE > ParentClass::assimilate( reinterpret_cast< ParentClass && >( src ) ); INDEX_TYPE const numSets = size(); + ArrayOfArraysView< T, INDEX_TYPE const, true, BUFFER_TYPE > const view = + ArrayOfArraysView< T, INDEX_TYPE const, true, BUFFER_TYPE >( numSets, + this->m_offsets, + this->m_sizes, + this->m_values ); + BUFFER_TYPE< INDEX_TYPE > const sizes = this->m_sizes; + if( desc == sortedArrayManipulation::UNSORTED_NO_DUPLICATES ) { - for( INDEX_TYPE i = 0; i < numSets; ++i ) - { - T * const setValues = getSetValues( i ); - INDEX_TYPE const numValues = sizeOfSet( i ); - std::sort( setValues, setValues + numValues ); - } + RAJA::forall< POLICY >( RAJA::TypedRangeSegment< INDEX_TYPE >( 0, numSets ), + [view] LVARRAY_HOST_DEVICE ( INDEX_TYPE const i ) + { + ArraySlice< T, 1, 0, INDEX_TYPE > const setValues = view[i]; + sortedArrayManipulation::makeSorted( setValues.begin(), setValues.end() ); + } ); } - if( desc == sortedArrayManipulation::SORTED_WITH_DUPLICATES ) + else if( desc == sortedArrayManipulation::SORTED_WITH_DUPLICATES ) { - for( INDEX_TYPE i = 0; i < numSets; ++i ) - { - T * const setValues = getSetValues( i ); - INDEX_TYPE const numValues = sizeOfSet( i ); - - INDEX_TYPE const numUniqueValues = sortedArrayManipulation::removeDuplicates( setValues, setValues + numValues ); - arrayManipulation::resize( setValues, numValues, numUniqueValues ); - this->m_sizes[ i ] = numUniqueValues; - } + RAJA::forall< POLICY >( RAJA::TypedRangeSegment< INDEX_TYPE >( 0, numSets ), + [view, sizes] LVARRAY_HOST_DEVICE ( INDEX_TYPE const i ) + { + ArraySlice< T, 1, 0, INDEX_TYPE > const setValues = view[i]; + INDEX_TYPE const numUniqueValues = sortedArrayManipulation::removeDuplicates( setValues.begin(), setValues.end() ); + arrayManipulation::resize< T >( setValues, setValues.size(), numUniqueValues ); + sizes[ i ] = numUniqueValues; + } ); } - if( desc == sortedArrayManipulation::UNSORTED_WITH_DUPLICATES ) + else if( desc == sortedArrayManipulation::UNSORTED_WITH_DUPLICATES ) { - for( INDEX_TYPE i = 0; i < numSets; ++i ) - { - T * const setValues = getSetValues( i ); - INDEX_TYPE const numValues = sizeOfSet( i ); - - INDEX_TYPE const numUniqueValues = sortedArrayManipulation::makeSortedUnique( setValues, setValues + numValues ); - arrayManipulation::resize( setValues, numValues, numUniqueValues ); - this->m_sizes[ i ] = numUniqueValues; - } + RAJA::forall< POLICY >( RAJA::TypedRangeSegment< INDEX_TYPE >( 0, numSets ), + [view, sizes] LVARRAY_HOST_DEVICE ( INDEX_TYPE const i ) + { + ArraySlice< T, 1, 0, INDEX_TYPE > const setValues = view[ i ]; + INDEX_TYPE const numUniqueValues = sortedArrayManipulation::makeSortedUnique( setValues.begin(), setValues.end() ); + arrayManipulation::resize< T >( setValues, setValues.size(), numUniqueValues ); + sizes[ i ] = numUniqueValues; + } ); } #ifdef ARRAY_BOUNDS_CHECK diff --git a/unitTests/testArrayOfArrays.cpp b/unitTests/testArrayOfArrays.cpp index de2f6bb8..784fd448 100644 --- a/unitTests/testArrayOfArrays.cpp +++ b/unitTests/testArrayOfArrays.cpp @@ -233,6 +233,41 @@ class ArrayOfArraysTest : public ::testing::Test COMPARE_TO_REFERENCE; } + void resizeFromOffsets( IndexType const newSize, IndexType const maxCapacity ) + { + COMPARE_TO_REFERENCE; + + std::vector< IndexType > newCapacities( newSize ); + + for( IndexType & capacity : newCapacities ) + { + capacity = rand( 0, maxCapacity ); + } + + std::vector< IndexType > newOffsets( newSize + 1 ); + + IndexType totalOffset = 0; + for( IndexType i = 0; i < newSize; ++i ) + { + newOffsets[i] = totalOffset; + totalOffset += newCapacities[i]; + } + newOffsets.back() = totalOffset; + + m_array.resizeFromOffsets( newSize, newOffsets.data() ); + + EXPECT_EQ( m_array.size(), newSize ); + for( IndexType i = 0; i < m_array.size(); ++i ) + { + EXPECT_EQ( m_array.sizeOfArray( i ), 0 ); + EXPECT_EQ( m_array.capacityOfArray( i ), newCapacities[ i ] ); + } + + m_ref.clear(); + m_ref.resize( newSize ); + COMPARE_TO_REFERENCE; + } + void resize() { COMPARE_TO_REFERENCE; @@ -749,6 +784,15 @@ TYPED_TEST( ArrayOfArraysTest, resizeFromCapacities ) } } +TYPED_TEST( ArrayOfArraysTest, resizeFromOffsets ) +{ + for( int i = 0; i < 3; ++i ) + { + this->resizeFromOffsets( 100, 10 ); + this->emplace( 10 ); + } +} + TYPED_TEST( ArrayOfArraysTest, resizeArray ) { this->resize( 100 ); diff --git a/unitTests/testArrayOfSets.cpp b/unitTests/testArrayOfSets.cpp index 364a2d0d..d3b9f540 100644 --- a/unitTests/testArrayOfSets.cpp +++ b/unitTests/testArrayOfSets.cpp @@ -429,45 +429,6 @@ class ArrayOfSetsTest : public ::testing::Test } } - void assimilate( IndexType const maxValue, IndexType const maxInserts, sortedArrayManipulation::Description const desc ) - { - IndexType const nSets = m_array.size(); - ArrayOfArraysT arrayToSteal( nSets ); - - for( IndexType i = 0; i < nSets; ++i ) - { - IndexType const nValues = rand( 0, maxInserts ); - - for( IndexType j = 0; j < nValues; ++j ) - { - T const value = T( rand( 0, maxValue )); - bool const insertSuccess = m_ref[ i ].insert( value ).second; - - if( sortedArrayManipulation::isUnique( desc )) - { - if( insertSuccess ) - { - arrayToSteal.emplaceBack( i, value ); - } - } - else - { - arrayToSteal.emplaceBack( i, value ); - } - } - - if( sortedArrayManipulation::isSorted( desc )) - { - T * const values = arrayToSteal[ i ]; - std::sort( values, values + arrayToSteal.sizeOfArray( i )); - } - } - - m_array.assimilate( std::move( arrayToSteal ), desc ); - - COMPARE_TO_REFERENCE - } - protected: template< typename CONTAINER > @@ -691,30 +652,6 @@ TYPED_TEST( ArrayOfSetsTest, shallowCopy ) this->shallowCopy(); } -TYPED_TEST( ArrayOfSetsTest, assimilateSortedUnique ) -{ - this->resize( 50 ); - this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::SORTED_UNIQUE ); -} - -TYPED_TEST( ArrayOfSetsTest, assimilateUnsortedNoDuplicates ) -{ - this->resize( 50 ); - this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::UNSORTED_NO_DUPLICATES ); -} - -TYPED_TEST( ArrayOfSetsTest, assimilateSortedWithDuplicates ) -{ - this->resize( 50 ); - this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::SORTED_WITH_DUPLICATES ); -} - -TYPED_TEST( ArrayOfSetsTest, assimilateUnsortedWithDuplicates ) -{ - this->resize( 50 ); - this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::UNSORTED_WITH_DUPLICATES ); -} - // This is testing capabilities of the ArrayOfArrays class, however it needs to first populate // the ArrayOfSets so it involves less code duplication to put it here. TYPED_TEST( ArrayOfSetsTest, ArrayOfArraysStealFrom ) @@ -754,6 +691,7 @@ class ArrayOfSetsViewTest : public ArrayOfSetsTest< typename ARRAY_OF_SETS_POLIC using typename ParentClass::IndexType; using typename ParentClass::ViewType; using typename ParentClass::ViewTypeConst; + using typename ParentClass::ArrayOfArraysT; template< typename U > using Array1D = typename ToArray< U, ARRAY_OF_SETS >::OneD; @@ -890,6 +828,47 @@ class ArrayOfSetsViewTest : public ArrayOfSetsTest< typename ARRAY_OF_SETS_POLIC COMPARE_TO_REFERENCE } + // This is not a View test, but it needs a POLICY type argument, so it's placed here for convenience + void assimilate( IndexType const maxValue, IndexType const maxInserts, sortedArrayManipulation::Description const desc ) + { + IndexType const nSets = m_array.size(); + ArrayOfArraysT arrayToSteal( nSets ); + + for( IndexType i = 0; i < nSets; ++i ) + { + IndexType const nValues = rand( 0, maxInserts ); + + for( IndexType j = 0; j < nValues; ++j ) + { + T const value = T( rand( 0, maxValue )); + bool const insertSuccess = m_ref[ i ].insert( value ).second; + + if( sortedArrayManipulation::isUnique( desc )) + { + if( insertSuccess ) + { + arrayToSteal.emplaceBack( i, value ); + } + } + else + { + arrayToSteal.emplaceBack( i, value ); + } + } + + if( sortedArrayManipulation::isSorted( desc )) + { + T * const values = arrayToSteal[ i ]; + std::sort( values, values + arrayToSteal.sizeOfArray( i )); + } + } + + m_array.template assimilate< POLICY >( std::move( arrayToSteal ), desc ); + + m_array.move( MemorySpace::host ); + COMPARE_TO_REFERENCE + } + protected: Array1D< Array1D< T > > createValues( bool const insert, bool const sortedUnique ) @@ -1012,6 +991,30 @@ TYPED_TEST( ArrayOfSetsViewTest, removeMultiple ) } } +TYPED_TEST( ArrayOfSetsViewTest, assimilateSortedUnique ) +{ + this->resize( 50 ); + this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::SORTED_UNIQUE ); +} + +TYPED_TEST( ArrayOfSetsViewTest, assimilateUnsortedNoDuplicates ) +{ + this->resize( 50 ); + this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::UNSORTED_NO_DUPLICATES ); +} + +TYPED_TEST( ArrayOfSetsViewTest, assimilateSortedWithDuplicates ) +{ + this->resize( 50 ); + this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::SORTED_WITH_DUPLICATES ); +} + +TYPED_TEST( ArrayOfSetsViewTest, assimilateUnsortedWithDuplicates ) +{ + this->resize( 50 ); + this->assimilate( DEFAULT_MAX_INSERTS, DEFAULT_MAX_VALUE, sortedArrayManipulation::UNSORTED_WITH_DUPLICATES ); +} + } // namespace testing } // namespace LvArray