Files
lammps/lib/kokkos/core/unit_test/TestAtomicViews.hpp
2018-03-08 10:57:08 -07:00

1481 lines
45 KiB
C++

/*
//@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 Christian R. Trott (crtrott@sandia.gov)
//
// ************************************************************************
//@HEADER
*/
#include <Kokkos_Core.hpp>
namespace TestAtomicViews {
//-------------------------------------------------
//-----------atomic view api tests-----------------
//-------------------------------------------------
template< class T, class ... P >
size_t allocation_count( const Kokkos::View< T, P... > & view )
{
const size_t card = view.size();
const size_t alloc = view.span();
const int memory_span = Kokkos::View< int* >::required_allocation_size( 100 );
return ( card <= alloc && memory_span == 400 ) ? alloc : 0;
}
template< class DataType,
class DeviceType,
unsigned Rank = Kokkos::ViewTraits< DataType >::rank >
struct TestViewOperator_LeftAndRight;
template< class DataType, class DeviceType >
struct TestViewOperator_LeftAndRight< DataType, DeviceType, 1 >
{
typedef typename DeviceType::execution_space execution_space;
typedef typename DeviceType::memory_space memory_space;
typedef typename execution_space::size_type size_type;
typedef int value_type;
KOKKOS_INLINE_FUNCTION
static void join( volatile value_type & update,
const volatile value_type & input )
{ update |= input; }
KOKKOS_INLINE_FUNCTION
static void init( value_type & update )
{ update = 0; }
typedef Kokkos::View< DataType, Kokkos::LayoutLeft, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > left_view;
typedef Kokkos::View< DataType, Kokkos::LayoutRight, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > right_view;
typedef Kokkos::View< DataType, Kokkos::LayoutStride, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > stride_view;
left_view left;
right_view right;
stride_view left_stride;
stride_view right_stride;
long left_alloc;
long right_alloc;
TestViewOperator_LeftAndRight()
: left( "left" )
, right( "right" )
, left_stride( left )
, right_stride( right )
, left_alloc( allocation_count( left ) )
, right_alloc( allocation_count( right ) )
{}
static void testit()
{
TestViewOperator_LeftAndRight driver;
int error_flag = 0;
Kokkos::parallel_reduce( 1, driver, error_flag );
ASSERT_EQ( error_flag, 0 );
}
KOKKOS_INLINE_FUNCTION
void operator()( const size_type, value_type & update ) const
{
for ( unsigned i0 = 0; i0 < unsigned( left.extent(0) ); ++i0 )
{
// Below checks that values match, but unable to check the references.
// Should this be able to be checked?
#ifdef KOKKOS_ENABLE_DEPRECATED_CODE
if ( left( i0 ) != left( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
if ( right( i0 ) != right( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
#else
if ( left( i0 ) != left.access( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
if ( right( i0 ) != right.access( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
#endif
if ( left( i0 ) != left_stride( i0 ) ) { update |= 4; }
if ( right( i0 ) != right_stride( i0 ) ) { update |= 8; }
/*
if ( &left( i0 ) != &left( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
if ( &right( i0 ) != &right( i0, 0, 0, 0, 0, 0, 0, 0 ) ) { update |= 3; }
if ( &left( i0 ) != &left_stride( i0 ) ) { update |= 4; }
if ( &right( i0 ) != &right_stride( i0 ) ) { update |= 8; }
*/
}
}
};
template< typename T, class DeviceType >
class TestAtomicViewAPI
{
public:
typedef DeviceType device;
enum { N0 = 1000,
N1 = 3,
N2 = 5,
N3 = 7 };
typedef Kokkos::View< T, device > dView0;
typedef Kokkos::View< T*, device > dView1;
typedef Kokkos::View< T*[N1], device > dView2;
typedef Kokkos::View< T*[N1][N2], device > dView3;
typedef Kokkos::View< T*[N1][N2][N3], device > dView4;
typedef Kokkos::View< const T*[N1][N2][N3], device > const_dView4;
typedef Kokkos::View< T****, device, Kokkos::MemoryUnmanaged > dView4_unmanaged;
typedef typename dView0::host_mirror_space host;
typedef Kokkos::View< T, device, Kokkos::MemoryTraits< Kokkos::Atomic > > aView0;
typedef Kokkos::View< T*, device, Kokkos::MemoryTraits< Kokkos::Atomic > > aView1;
typedef Kokkos::View< T*[N1], device, Kokkos::MemoryTraits< Kokkos::Atomic > > aView2;
typedef Kokkos::View< T*[N1][N2], device, Kokkos::MemoryTraits< Kokkos::Atomic > > aView3;
typedef Kokkos::View< T*[N1][N2][N3], device, Kokkos::MemoryTraits< Kokkos::Atomic > > aView4;
typedef Kokkos::View< const T*[N1][N2][N3], device, Kokkos::MemoryTraits< Kokkos::Atomic > > const_aView4;
typedef Kokkos::View< T****, device, Kokkos::MemoryTraits< Kokkos::Unmanaged | Kokkos::Atomic > > aView4_unmanaged;
typedef typename aView0::host_mirror_space host_atomic;
TestAtomicViewAPI()
{
TestViewOperator_LeftAndRight< int[2], device >::testit();
run_test_rank0();
run_test_rank4();
run_test_const();
}
static void run_test_rank0()
{
dView0 dx, dy;
aView0 ax, ay, az;
dx = dView0( "dx" );
dy = dView0( "dy" );
ASSERT_EQ( dx.use_count(), size_t( 1 ) );
ASSERT_EQ( dy.use_count(), size_t( 1 ) );
ax = dx;
ay = dy;
ASSERT_EQ( dx.use_count(), size_t( 2 ) );
ASSERT_EQ( dy.use_count(), size_t( 2 ) );
ASSERT_EQ( dx.use_count(), ax.use_count() );
az = ax;
ASSERT_EQ( dx.use_count(), size_t( 3 ) );
ASSERT_EQ( ax.use_count(), size_t( 3 ) );
ASSERT_EQ( az.use_count(), size_t( 3 ) );
ASSERT_EQ( az.use_count(), ax.use_count() );
}
static void run_test_rank4()
{
dView4 dx, dy;
aView4 ax, ay, az;
dx = dView4( "dx", N0 );
dy = dView4( "dy", N0 );
ASSERT_EQ( dx.use_count(), size_t( 1 ) );
ASSERT_EQ( dy.use_count(), size_t( 1 ) );
ax = dx;
ay = dy;
ASSERT_EQ( dx.use_count(), size_t( 2 ) );
ASSERT_EQ( dy.use_count(), size_t( 2 ) );
ASSERT_EQ( dx.use_count(), ax.use_count() );
dView4_unmanaged unmanaged_dx = dx;
ASSERT_EQ( dx.use_count(), size_t( 2 ) );
az = ax;
ASSERT_EQ( dx.use_count(), size_t( 3 ) );
ASSERT_EQ( ax.use_count(), size_t( 3 ) );
ASSERT_EQ( az.use_count(), size_t( 3 ) );
ASSERT_EQ( az.use_count(), ax.use_count() );
aView4_unmanaged unmanaged_ax = ax;
ASSERT_EQ( ax.use_count(), size_t( 3 ) );
aView4_unmanaged unmanaged_ax_from_ptr_dx =
aView4_unmanaged( dx.data(), dx.extent(0), dx.extent(1), dx.extent(2), dx.extent(3) );
ASSERT_EQ( ax.use_count(), size_t( 3 ) );
const_aView4 const_ax = ax;
ASSERT_EQ( ax.use_count(), size_t( 4 ) );
ASSERT_EQ( const_ax.use_count(), ax.use_count() );
ASSERT_FALSE( ax.data() == 0 );
ASSERT_FALSE( const_ax.data() == 0 ); // referenceable ptr
ASSERT_FALSE( unmanaged_ax.data() == 0 );
ASSERT_FALSE( unmanaged_ax_from_ptr_dx.data() == 0 );
ASSERT_FALSE( ay.data() == 0 );
// ASSERT_NE( ax, ay );
// Above test results in following runtime error from gtest:
// Expected: (ax) != (ay), actual: 32-byte object <30-01 D0-A0 D8-7F 00-00 00-31 44-0C 01-00 00-00 E8-03 00-00 00-00 00-00 69-00 00-00 00-00 00-00> vs 32-byte object <80-01 D0-A0 D8-7F 00-00 00-A1 4A-0C 01-00 00-00 E8-03 00-00 00-00 00-00 69-00 00-00 00-00 00-00>
ASSERT_EQ( ax.extent(0), unsigned( N0 ) );
ASSERT_EQ( ax.extent(1), unsigned( N1 ) );
ASSERT_EQ( ax.extent(2), unsigned( N2 ) );
ASSERT_EQ( ax.extent(3), unsigned( N3 ) );
ASSERT_EQ( ay.extent(0), unsigned( N0 ) );
ASSERT_EQ( ay.extent(1), unsigned( N1 ) );
ASSERT_EQ( ay.extent(2), unsigned( N2 ) );
ASSERT_EQ( ay.extent(3), unsigned( N3 ) );
ASSERT_EQ( unmanaged_ax_from_ptr_dx.span(), unsigned( N0 ) * unsigned( N1 ) * unsigned( N2 ) * unsigned( N3 ) );
}
typedef T DataType[2];
static void
check_auto_conversion_to_const(
const Kokkos::View< const DataType, device, Kokkos::MemoryTraits<Kokkos::Atomic> > & arg_const,
const Kokkos::View< const DataType, device, Kokkos::MemoryTraits<Kokkos::Atomic> > & arg )
{
ASSERT_TRUE( arg_const == arg );
}
static void run_test_const()
{
typedef Kokkos::View< DataType, device, Kokkos::MemoryTraits<Kokkos::Atomic> > typeX;
typedef Kokkos::View< const DataType, device, Kokkos::MemoryTraits<Kokkos::Atomic> > const_typeX;
typeX x( "X" );
const_typeX xc = x;
//ASSERT_TRUE( xc == x ); // const xc is referenceable, non-const x is not
//ASSERT_TRUE( x == xc );
check_auto_conversion_to_const( x, xc );
}
};
//---------------------------------------------------
//-----------initialization functors-----------------
//---------------------------------------------------
template<class T, class execution_space >
struct InitFunctor_Seq {
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
const long length;
InitFunctor_Seq( view_type & input_, const long length_ )
: input( input_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
input( i ) = (T) i;
}
}
};
template<class T, class execution_space >
struct InitFunctor_ModTimes {
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
const long length;
const long remainder;
InitFunctor_ModTimes( view_type & input_, const long length_, const long remainder_ )
: input( input_ )
, length( length_ )
, remainder( remainder_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % ( remainder + 1 ) == remainder ) {
input( i ) = (T) 2;
}
else {
input( i ) = (T) 1;
}
}
}
};
template<class T, class execution_space >
struct InitFunctor_ModShift {
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
const long length;
const long remainder;
InitFunctor_ModShift( view_type & input_, const long length_, const long remainder_ )
: input( input_ )
, length( length_ )
, remainder( remainder_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % ( remainder + 1 ) == remainder ) {
input( i ) = 1;
}
}
}
};
//---------------------------------------------------
//-----------atomic view plus-equal------------------
//---------------------------------------------------
template<class T, class execution_space >
struct PlusEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type even_odd_result;
const long length;
// Wrap the result view in an atomic view, use this for operator
PlusEqualAtomicViewFunctor( const view_type & input_, view_type & even_odd_result_, const long length_ )
: input( input_ )
, even_odd_result( even_odd_result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 2 == 0 ) {
even_odd_result( 0 ) += input( i );
}
else {
even_odd_result( 1 ) += input( i );
}
}
}
};
template< class T, class execution_space >
T PlusEqualAtomicView( const long input_length ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 2 );
InitFunctor_Seq< T, execution_space > init_f( input, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
PlusEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy<execution_space>( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) + h_result_view( 1 ) );
}
template< class T >
T PlusEqualAtomicViewCheck( const long input_length ) {
const long N = input_length;
T result[2];
if ( N % 2 == 0 ) {
const long half_sum_end = ( N / 2 ) - 1;
const long full_sum_end = N - 1;
result[0] = half_sum_end * ( half_sum_end + 1 ) / 2; // Even sum.
result[1] = ( full_sum_end * ( full_sum_end + 1 ) / 2 ) - result[0]; // Odd sum.
}
else {
const long half_sum_end = (T) ( N / 2 );
const long full_sum_end = N - 2;
result[0] = half_sum_end * ( half_sum_end - 1 ) / 2; // Even sum.
result[1] = ( full_sum_end * ( full_sum_end - 1 ) / 2 ) - result[0]; // Odd sum.
}
return (T) ( result[0] + result[1] );
}
template< class T, class DeviceType >
bool PlusEqualAtomicViewTest( long input_length )
{
T res = PlusEqualAtomicView< T, DeviceType >( input_length );
T resSerial = PlusEqualAtomicViewCheck< T >( input_length );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = PlusEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//-----------atomic view minus-equal-----------------
//---------------------------------------------------
template<class T, class execution_space >
struct MinusEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type even_odd_result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
MinusEqualAtomicViewFunctor( const view_type & input_, view_type & even_odd_result_, const long length_ )
: input( input_ )
, even_odd_result( even_odd_result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 2 == 0 ) {
even_odd_result( 0 ) -= input( i );
}
else {
even_odd_result( 1 ) -= input( i );
}
}
}
};
template< class T, class execution_space >
T MinusEqualAtomicView( const long input_length ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 2 );
InitFunctor_Seq< T, execution_space > init_f( input, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
MinusEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) + h_result_view( 1 ) );
}
template< class T >
T MinusEqualAtomicViewCheck( const long input_length ) {
const long N = input_length;
T result[2];
if ( N % 2 == 0 ) {
const long half_sum_end = ( N / 2 ) - 1;
const long full_sum_end = N - 1;
result[0] = -1 * ( half_sum_end * ( half_sum_end + 1 ) / 2 ); // Even sum.
result[1] = -1 * ( ( full_sum_end * ( full_sum_end + 1 ) / 2 ) + result[0] ); // Odd sum.
}
else {
const long half_sum_end = (long) ( N / 2 );
const long full_sum_end = N - 2;
result[0] = -1 * ( half_sum_end * ( half_sum_end - 1 ) / 2 ); // Even sum.
result[1] = -1 * ( ( full_sum_end * ( full_sum_end - 1 ) / 2 ) + result[0] ); // Odd sum.
}
return ( result[0] + result[1] );
}
template< class T, class DeviceType >
bool MinusEqualAtomicViewTest( long input_length )
{
T res = MinusEqualAtomicView< T, DeviceType >( input_length );
T resSerial = MinusEqualAtomicViewCheck< T >( input_length );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = MinusEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//-----------atomic view times-equal-----------------
//---------------------------------------------------
template<class T, class execution_space >
struct TimesEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type result;
const long length;
// Wrap the result view in an atomic view, use this for operator
TimesEqualAtomicViewFunctor( const view_type & input_, view_type & result_, const long length_ )
: input( input_ )
, result( result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length && i > 0 ) {
result( 0 ) *= (double) input( i );
}
}
};
template< class T, class execution_space >
T TimesEqualAtomicView( const long input_length, const long remainder ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 1 );
deep_copy( result_view, 1.0 );
InitFunctor_ModTimes< T, execution_space > init_f( input, length, remainder );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
TimesEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) );
}
template< class T >
T TimesEqualAtomicViewCheck( const long input_length, const long remainder ) {
// Analytical result.
const long N = input_length;
T result = 1.0;
for ( long i = 2; i < N; ++i ) {
if ( i % ( remainder + 1 ) == remainder ) {
result *= 2.0;
}
else {
result *= 1.0;
}
}
return (T) result;
}
template< class T, class DeviceType>
bool TimesEqualAtomicViewTest( const long input_length )
{
const long remainder = 23;
T res = TimesEqualAtomicView< T, DeviceType >( input_length, remainder );
T resSerial = TimesEqualAtomicViewCheck< T >( input_length, remainder );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = TimesEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//------------atomic view div-equal------------------
//---------------------------------------------------
template<class T, class execution_space >
struct DivEqualAtomicViewFunctor {
typedef Kokkos::View< T, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T, execution_space > scalar_view_type;
view_type input;
atomic_view_type result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
DivEqualAtomicViewFunctor( const view_type & input_, scalar_view_type & result_, const long length_ )
: input( input_ )
, result( result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length && i > 0 ) {
result() /= (double) ( input( i ) );
}
}
};
template< class T, class execution_space >
T DivEqualAtomicView( const long input_length, const long remainder ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T, execution_space > scalar_view_type;
typedef typename scalar_view_type::HostMirror host_scalar_view_type;
const long length = input_length;
view_type input( "input_view", length );
scalar_view_type result_view( "result_view" );
Kokkos::deep_copy( result_view, 12121212121 );
InitFunctor_ModTimes< T, execution_space > init_f( input, length, remainder );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
DivEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_scalar_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view() );
}
template< class T >
T DivEqualAtomicViewCheck( const long input_length, const long remainder ) {
const long N = input_length;
T result = 12121212121.0;
for ( long i = 2; i < N; ++i ) {
if ( i % ( remainder + 1 ) == remainder ) {
result /= 1.0;
}
else {
result /= 2.0;
}
}
return (T) result;
}
template< class T, class DeviceType >
bool DivEqualAtomicViewTest( const long input_length )
{
const long remainder = 23;
T res = DivEqualAtomicView< T, DeviceType >( input_length, remainder );
T resSerial = DivEqualAtomicViewCheck< T >( input_length, remainder );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = DivEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//------------atomic view mod-equal------------------
//---------------------------------------------------
template< class T, class execution_space >
struct ModEqualAtomicViewFunctor {
typedef Kokkos::View< T, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T, execution_space > scalar_view_type;
view_type input;
atomic_view_type result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
ModEqualAtomicViewFunctor( const view_type & input_, scalar_view_type & result_, const long length_ )
: input( input_ )
, result( result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length && i > 0 ) {
result() %= (double) ( input( i ) );
}
}
};
template< class T, class execution_space >
T ModEqualAtomicView( const long input_length, const long remainder ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T, execution_space > scalar_view_type;
typedef typename scalar_view_type::HostMirror host_scalar_view_type;
const long length = input_length;
view_type input( "input_view", length );
scalar_view_type result_view( "result_view" );
Kokkos::deep_copy( result_view, 12121212121 );
InitFunctor_ModTimes< T, execution_space > init_f( input, length, remainder );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
ModEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_scalar_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view() );
}
template< class T >
T ModEqualAtomicViewCheck( const long input_length, const long remainder ) {
const long N = input_length;
T result = 12121212121;
for ( long i = 2; i < N; ++i ) {
if ( i % ( remainder + 1 ) == remainder ) {
result %= 1;
}
else {
result %= 2;
}
}
return (T) result;
}
template< class T, class DeviceType >
bool ModEqualAtomicViewTest( const long input_length )
{
static_assert( std::is_integral< T >::value, "ModEqualAtomicView Error: Type must be integral type for this unit test" );
const long remainder = 23;
T res = ModEqualAtomicView< T, DeviceType >( input_length, remainder );
T resSerial = ModEqualAtomicViewCheck< T >( input_length, remainder );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = ModEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//------------atomic view rs-equal------------------
//---------------------------------------------------
template< class T, class execution_space >
struct RSEqualAtomicViewFunctor {
typedef Kokkos::View< T****, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T****, execution_space > result_view_type;
const view_type input;
atomic_view_type result;
const long length;
const long value;
// Wrap the result view in an atomic view, use this for operator.
RSEqualAtomicViewFunctor( const view_type & input_, result_view_type & result_, const long & length_, const long & value_ )
: input( input_ )
, result( result_ )
, length( length_ )
, value( value_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 4 == 0 ) {
result( 1, 0, 0, 0 ) >>= input( i );
}
else if ( i % 4 == 1 ) {
result( 0, 1, 0, 0 ) >>= input( i );
}
else if ( i % 4 == 2 ) {
result( 0, 0, 1, 0 ) >>= input( i );
}
else if ( i % 4 == 3 ) {
result( 0, 0, 0, 1 ) >>= input( i );
}
}
}
};
template< class T, class execution_space >
T RSEqualAtomicView( const long input_length, const long value, const long remainder ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T****, execution_space > result_view_type;
typedef typename result_view_type::HostMirror host_scalar_view_type;
const long length = input_length;
view_type input( "input_view", length );
result_view_type result_view( "result_view", 2, 2, 2, 2 );
host_scalar_view_type h_result_view = Kokkos::create_mirror_view( result_view );
h_result_view( 1, 0, 0, 0 ) = value;
h_result_view( 0, 1, 0, 0 ) = value;
h_result_view( 0, 0, 1, 0 ) = value;
h_result_view( 0, 0, 0, 1 ) = value;
Kokkos::deep_copy( result_view, h_result_view );
InitFunctor_ModShift< T, execution_space > init_f( input, length, remainder );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
RSEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length, value );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 1, 0, 0, 0 ) );
}
template< class T >
T RSEqualAtomicViewCheck( const long input_length, const long value, const long remainder ) {
T result[4];
result[0] = value;
result[1] = value;
result[2] = value;
result[3] = value;
T * input = new T[input_length];
for ( long i = 0; i < input_length; ++i ) {
if ( i % ( remainder + 1 ) == remainder ) {
input[i] = 1;
}
else {
input[i] = 0;
}
}
for ( long i = 0; i < input_length; ++i ) {
if ( i % 4 == 0 ) {
result[0] >>= input[i];
}
else if ( i % 4 == 1 ) {
result[1] >>= input[i];
}
else if ( i % 4 == 2 ) {
result[2] >>= input[i];
}
else if ( i % 4 == 3 ) {
result[3] >>= input[i];
}
}
delete [] input;
return (T) result[0];
}
template< class T, class DeviceType >
bool RSEqualAtomicViewTest( const long input_length )
{
static_assert( std::is_integral< T >::value, "RSEqualAtomicViewTest: Must be integral type for test" );
const long remainder = 61042; //prime - 1
const long value = 1073741825; // 2^30+1
T res = RSEqualAtomicView< T, DeviceType >( input_length, value, remainder );
T resSerial = RSEqualAtomicViewCheck< T >( input_length, value, remainder );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = RSEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//------------atomic view ls-equal------------------
//---------------------------------------------------
template<class T, class execution_space >
struct LSEqualAtomicViewFunctor {
typedef Kokkos::View< T****, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T****, execution_space > result_view_type;
view_type input;
atomic_view_type result;
const long length;
const long value;
// Wrap the result view in an atomic view, use this for operator.
LSEqualAtomicViewFunctor( const view_type & input_, result_view_type & result_, const long & length_, const long & value_ )
: input( input_ )
, result( result_ )
, length( length_ )
, value( value_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 4 == 0 ) {
result( 1, 0, 0, 0 ) <<= input( i );
}
else if ( i % 4 == 1 ) {
result( 0, 1, 0, 0 ) <<= input( i );
}
else if ( i % 4 == 2 ) {
result( 0, 0, 1, 0 ) <<= input( i );
}
else if ( i % 4 == 3 ) {
result( 0, 0, 0, 1 ) <<= input( i );
}
}
}
};
template< class T, class execution_space >
T LSEqualAtomicView( const long input_length, const long value, const long remainder ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef Kokkos::View< T****, execution_space > result_view_type;
typedef typename result_view_type::HostMirror host_scalar_view_type;
const long length = input_length;
view_type input( "input_view", length );
result_view_type result_view( "result_view", 2, 2, 2, 2 );
host_scalar_view_type h_result_view = Kokkos::create_mirror_view( result_view );
h_result_view( 1, 0, 0, 0 ) = value;
h_result_view( 0, 1, 0, 0 ) = value;
h_result_view( 0, 0, 1, 0 ) = value;
h_result_view( 0, 0, 0, 1 ) = value;
Kokkos::deep_copy( result_view, h_result_view );
InitFunctor_ModShift< T, execution_space > init_f( input, length, remainder );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
LSEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length, value );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 1, 0, 0, 0 ) );
}
template< class T >
T LSEqualAtomicViewCheck( const long input_length, const long value, const long remainder ) {
T result[4];
result[0] = value;
result[1] = value;
result[2] = value;
result[3] = value;
T * input = new T[input_length];
for ( long i = 0; i < input_length; ++i ) {
if ( i % ( remainder + 1 ) == remainder ) {
input[i] = 1;
}
else {
input[i] = 0;
}
}
for ( long i = 0; i < input_length; ++i ) {
if ( i % 4 == 0 ) {
result[0] <<= input[i];
}
else if ( i % 4 == 1 ) {
result[1] <<= input[i];
}
else if ( i % 4 == 2 ) {
result[2] <<= input[i];
}
else if ( i % 4 == 3 ) {
result[3] <<= input[i];
}
}
delete [] input;
return (T) result[0];
}
template< class T, class DeviceType >
bool LSEqualAtomicViewTest( const long input_length )
{
static_assert( std::is_integral< T >::value, "LSEqualAtomicViewTest: Must be integral type for test" );
const long remainder = 61042; //prime - 1
const long value = 1; // 2^30+1
T res = LSEqualAtomicView< T, DeviceType >( input_length, value, remainder );
T resSerial = LSEqualAtomicViewCheck< T >( input_length, value, remainder );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = RSEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//-----------atomic view and-equal-----------------
//---------------------------------------------------
template< class T, class execution_space >
struct AndEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type even_odd_result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
AndEqualAtomicViewFunctor( const view_type & input_, view_type & even_odd_result_, const long length_ )
: input( input_ )
, even_odd_result( even_odd_result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 2 == 0 ) {
even_odd_result( 0 ) &= input( i );
}
else {
even_odd_result( 1 ) &= input( i );
}
}
}
};
template< class T, class execution_space >
T AndEqualAtomicView( const long input_length ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 2 );
Kokkos::deep_copy( result_view, 1 );
InitFunctor_Seq< T, execution_space > init_f( input, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
AndEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) );
}
template< class T >
T AndEqualAtomicViewCheck( const long input_length ) {
const long N = input_length;
T result[2] = { 1 };
for ( long i = 0; i < N; ++i ) {
if ( N % 2 == 0 ) {
result[0] &= (T) i;
}
else {
result[1] &= (T) i;
}
}
return ( result[0] );
}
template< class T, class DeviceType >
bool AndEqualAtomicViewTest( long input_length )
{
static_assert( std::is_integral< T >::value, "AndEqualAtomicViewTest: Must be integral type for test" );
T res = AndEqualAtomicView< T, DeviceType >( input_length );
T resSerial = AndEqualAtomicViewCheck< T >( input_length );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = AndEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//-----------atomic view or-equal-----------------
//---------------------------------------------------
template< class T, class execution_space >
struct OrEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type even_odd_result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
OrEqualAtomicViewFunctor( const view_type & input_, view_type & even_odd_result_, const long length_ )
: input( input_ )
, even_odd_result( even_odd_result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 2 == 0 ) {
even_odd_result( 0 ) |= input( i );
}
else {
even_odd_result( 1 ) |= input( i );
}
}
}
};
template< class T, class execution_space >
T OrEqualAtomicView( const long input_length ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 2 );
InitFunctor_Seq< T, execution_space > init_f( input, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
OrEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) );
}
template< class T >
T OrEqualAtomicViewCheck( const long input_length ) {
const long N = input_length;
T result[2] = { 0 };
for ( long i = 0; i < N; ++i ) {
if ( i % 2 == 0 ) {
result[0] |= (T) i;
}
else {
result[1] |= (T) i;
}
}
return (T) ( result[0] );
}
template< class T, class DeviceType >
bool OrEqualAtomicViewTest( long input_length )
{
static_assert( std::is_integral< T >::value, "OrEqualAtomicViewTest: Must be integral type for test" );
T res = OrEqualAtomicView< T, DeviceType >( input_length );
T resSerial = OrEqualAtomicViewCheck< T >( input_length );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = OrEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
//---------------------------------------------------
//-----------atomic view xor-equal-----------------
//---------------------------------------------------
template< class T, class execution_space >
struct XOrEqualAtomicViewFunctor {
typedef Kokkos::View< T*, execution_space, Kokkos::MemoryTraits<Kokkos::Atomic> > atomic_view_type;
typedef Kokkos::View< T*, execution_space > view_type;
view_type input;
atomic_view_type even_odd_result;
const long length;
// Wrap the result view in an atomic view, use this for operator.
XOrEqualAtomicViewFunctor( const view_type & input_, view_type & even_odd_result_, const long length_ )
: input( input_ )
, even_odd_result( even_odd_result_ )
, length( length_ )
{}
KOKKOS_INLINE_FUNCTION
void operator()( const long i ) const {
if ( i < length ) {
if ( i % 2 == 0 ) {
even_odd_result( 0 ) ^= input( i );
}
else {
even_odd_result( 1 ) ^= input( i );
}
}
}
};
template< class T, class execution_space >
T XOrEqualAtomicView( const long input_length ) {
typedef Kokkos::View< T*, execution_space > view_type;
typedef typename view_type::HostMirror host_view_type;
const long length = input_length;
view_type input( "input_view", length );
view_type result_view( "result_view", 2 );
InitFunctor_Seq< T, execution_space > init_f( input, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), init_f );
XOrEqualAtomicViewFunctor< T, execution_space > functor( input, result_view, length );
Kokkos::parallel_for( Kokkos::RangePolicy< execution_space >( 0, length ), functor );
Kokkos::fence();
host_view_type h_result_view = Kokkos::create_mirror_view( result_view );
Kokkos::deep_copy( h_result_view, result_view );
return (T) ( h_result_view( 0 ) );
}
template< class T >
T XOrEqualAtomicViewCheck( const long input_length ) {
const long N = input_length;
T result[2] = { 0 };
for ( long i = 0; i < N; ++i ) {
if ( i % 2 == 0 ) {
result[0] ^= (T) i;
}
else {
result[1] ^= (T) i;
}
}
return (T) ( result[0] );
}
template< class T, class DeviceType >
bool XOrEqualAtomicViewTest( long input_length )
{
static_assert( std::is_integral< T >::value, "XOrEqualAtomicViewTest: Must be integral type for test" );
T res = XOrEqualAtomicView< T, DeviceType >( input_length );
T resSerial = XOrEqualAtomicViewCheck< T >( input_length );
bool passed = true;
if ( resSerial != res ) {
passed = false;
std::cout << "Loop<"
<< typeid( T ).name()
<< ">( test = XOrEqualAtomicViewTest"
<< " FAILED : "
<< resSerial << " != " << res
<< std::endl;
}
return passed;
}
// inc/dec?
//---------------------------------------------------
//--------------atomic_test_control------------------
//---------------------------------------------------
template< class T, class DeviceType >
bool AtomicViewsTestIntegralType( const int length, int test )
{
static_assert( std::is_integral< T >::value, "TestAtomicViews Error: Non-integral type passed into IntegralType tests" );
switch ( test ) {
case 1: return PlusEqualAtomicViewTest< T, DeviceType >( length );
case 2: return MinusEqualAtomicViewTest< T, DeviceType >( length );
case 3: return RSEqualAtomicViewTest< T, DeviceType >( length );
case 4: return LSEqualAtomicViewTest< T, DeviceType >( length );
case 5: return ModEqualAtomicViewTest< T, DeviceType >( length );
case 6: return AndEqualAtomicViewTest< T, DeviceType >( length );
case 7: return OrEqualAtomicViewTest< T, DeviceType >( length );
case 8: return XOrEqualAtomicViewTest< T, DeviceType >( length );
}
return 0;
}
template< class T, class DeviceType >
bool AtomicViewsTestNonIntegralType( const int length, int test )
{
switch ( test ) {
case 1: return PlusEqualAtomicViewTest< T, DeviceType >( length );
case 2: return MinusEqualAtomicViewTest< T, DeviceType >( length );
case 3: return TimesEqualAtomicViewTest< T, DeviceType >( length );
case 4: return DivEqualAtomicViewTest< T, DeviceType >( length );
}
return 0;
}
} // namespace TestAtomicViews
namespace Test {
TEST_F( TEST_CATEGORY, atomic_views_integral )
{
const long length = 1000000;
{
// Integral Types.
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 1 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 2 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 3 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 4 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 5 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 6 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 7 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestIntegralType< long, TEST_EXECSPACE >( length, 8 ) ) );
}
}
TEST_F( TEST_CATEGORY, atomic_views_nonintegral )
{
const long length = 1000000;
{
// Non-Integral Types.
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestNonIntegralType< double, TEST_EXECSPACE >( length, 1 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestNonIntegralType< double, TEST_EXECSPACE >( length, 2 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestNonIntegralType< double, TEST_EXECSPACE >( length, 3 ) ) );
ASSERT_TRUE( ( TestAtomicViews::AtomicViewsTestNonIntegralType< double, TEST_EXECSPACE >( length, 4 ) ) );
}
}
TEST_F( TEST_CATEGORY, atomic_view_api )
{
TestAtomicViews::TestAtomicViewAPI< int, TEST_EXECSPACE >();
}
}