Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements for ArrayOf[Arrays|Sets] construction/assimilation #256

Merged
merged 3 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/exampleArrayOfSets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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 );
Expand Down
1 change: 1 addition & 0 deletions src/ArrayOfArrays.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class ArrayOfArrays : protected ArrayOfArraysView< T, INDEX_TYPE, false, BUFFER_
}

using ParentClass::resizeFromCapacities;
using ParentClass::resizeFromOffsets;

///@}

Expand Down
94 changes: 63 additions & 31 deletions src/ArrayOfArraysView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <RAJA/RAJA.hpp>
Expand Down Expand Up @@ -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.
Expand All @@ -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 ),
Comment on lines -723 to -725
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casts seem no longer needed with current version of RAJA we're using

m_offsets[ 0 ] = 0;
RAJA::inclusive_scan< POLICY >( capacities,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should probably call resizeFromOffsets once the scan has been performed I think.

Copy link
Contributor Author

@klevzoff klevzoff Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I couldn't just call it, because the result of the scan is written straight into m_offsets, but its old values are still needed in resizeFromOffsets, plus it would then perform a self-copy of m_offsets. I ended up pulling all the shared code into a private function resizeFromOffsetsImpl, which both resizeFromOffsets and resizeFromCapacities delegate to, passing a callback that is invoked when new offsets must be populated.

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 ... );
}

///@}
Expand Down Expand Up @@ -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 */
60 changes: 33 additions & 27 deletions src/ArrayOfSets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand All @@ -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;
Copy link
Contributor Author

@klevzoff klevzoff Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be no way to manually "resize" a single sub-array through view after value deduplication. So I just capture sizes separately and overwrite them, seems to work well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me.


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
Expand Down
44 changes: 44 additions & 0 deletions unitTests/testArrayOfArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 );
Expand Down
Loading