//@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include namespace TestAtomicViews { //------------------------------------------------- //-----------atomic view api tests----------------- //------------------------------------------------- template size_t allocation_count(const Kokkos::View& view) { const size_t card = view.size(); const size_t alloc = view.span(); const int memory_span = Kokkos::View::required_allocation_size(100); return (card <= alloc && memory_span == 400) ? alloc : 0; } template ::rank> struct TestViewOperator_LeftAndRight; template struct TestViewOperator_LeftAndRight { using execution_space = typename DeviceType::execution_space; using memory_space = typename DeviceType::memory_space; using size_type = typename execution_space::size_type; using value_type = int; KOKKOS_INLINE_FUNCTION static void join(value_type& update, const value_type& input) { update |= input; } KOKKOS_INLINE_FUNCTION static void init(value_type& update) { update = 0; } using left_view = Kokkos::View >; using right_view = Kokkos::View >; using stride_view = Kokkos::View >; left_view left; right_view right; stride_view left_stride; stride_view right_stride; int64_t left_alloc; int64_t 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? 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; } 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 class TestAtomicViewAPI { public: using device = DeviceType; enum { N0 = 1000, N1 = 3, N2 = 5, N3 = 7 }; using dView0 = Kokkos::View; using dView1 = Kokkos::View; using dView2 = Kokkos::View; using dView3 = Kokkos::View; using dView4 = Kokkos::View; using const_dView4 = Kokkos::View; using dView4_unmanaged = Kokkos::View; using host = typename dView0::host_mirror_space; using aView0 = Kokkos::View >; using aView1 = Kokkos::View >; using aView2 = Kokkos::View >; using aView3 = Kokkos::View >; using aView4 = Kokkos::View >; using const_aView4 = Kokkos::View >; using aView4_unmanaged = Kokkos::View >; using host_atomic = typename aView0::host_mirror_space; TestAtomicViewAPI() { // FIXME_OPENMPTARGET #ifndef KOKKOS_ENABLE_OPENMPTARGET TestViewOperator_LeftAndRight::testit(); #endif 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(), 1); ASSERT_EQ(dy.use_count(), 1); ax = dx; ay = dy; ASSERT_EQ(dx.use_count(), 2); ASSERT_EQ(dy.use_count(), 2); ASSERT_EQ(dx.use_count(), ax.use_count()); az = ax; ASSERT_EQ(dx.use_count(), 3); ASSERT_EQ(ax.use_count(), 3); ASSERT_EQ(az.use_count(), 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(), 1); ASSERT_EQ(dy.use_count(), 1); ax = dx; ay = dy; ASSERT_EQ(dx.use_count(), 2); ASSERT_EQ(dy.use_count(), 2); ASSERT_EQ(dx.use_count(), ax.use_count()); dView4_unmanaged unmanaged_dx = dx; ASSERT_EQ(dx.use_count(), 2); az = ax; ASSERT_EQ(dx.use_count(), 3); ASSERT_EQ(ax.use_count(), 3); ASSERT_EQ(az.use_count(), 3); ASSERT_EQ(az.use_count(), ax.use_count()); aView4_unmanaged unmanaged_ax = ax; ASSERT_EQ(ax.use_count(), 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(), 3); const_aView4 const_ax = ax; ASSERT_EQ(ax.use_count(), 4); ASSERT_EQ(const_ax.use_count(), ax.use_count()); ASSERT_NE(ax.data(), nullptr); ASSERT_NE(const_ax.data(), nullptr); // referenceable ptr ASSERT_NE(unmanaged_ax.data(), nullptr); ASSERT_NE(unmanaged_ax_from_ptr_dx.data(), nullptr); ASSERT_NE(ay.data(), nullptr); // 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)); } using DataType = T[2]; static void check_auto_conversion_to_const( const Kokkos::View >& arg_const, const Kokkos::View >& arg) { ASSERT_EQ(arg_const, arg); } static void run_test_const() { using typeX = Kokkos::View >; using const_typeX = Kokkos::View >; typeX x("X"); const_typeX xc = x; // ASSERT_EQ( xc , x ); // const xc is referenceable, non-const x is not // ASSERT_EQ( x , xc ); check_auto_conversion_to_const(x, xc); } }; //--------------------------------------------------- //-----------initialization functors----------------- //--------------------------------------------------- template struct InitFunctor_Seq { using view_type = Kokkos::View; view_type input; const int64_t length; InitFunctor_Seq(view_type& input_, const int64_t length_) : input(input_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { input(i) = (T)i; } } }; template struct InitFunctor_ModTimes { using view_type = Kokkos::View; view_type input; const int64_t length; const int64_t remainder; InitFunctor_ModTimes(view_type& input_, const int64_t length_, const int64_t remainder_) : input(input_), length(length_), remainder(remainder_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % (remainder + 1) == remainder) { input(i) = (T)2; } else { input(i) = (T)1; } } } }; template struct InitFunctor_ModShift { using view_type = Kokkos::View; view_type input; const int64_t length; const int64_t remainder; InitFunctor_ModShift(view_type& input_, const int64_t length_, const int64_t remainder_) : input(input_), length(length_), remainder(remainder_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % (remainder + 1) == remainder) { input(i) = 1; } } } }; //--------------------------------------------------- //-----------atomic view plus-equal------------------ //--------------------------------------------------- template struct PlusEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type even_odd_result; const int64_t length; // Wrap the result view in an atomic view, use this for operator PlusEqualAtomicViewFunctor(const view_type& input_, view_type& even_odd_result_, const int64_t length_) : input(input_), even_odd_result(even_odd_result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % 2 == 0) { even_odd_result(0) += input(i); } else { even_odd_result(1) += input(i); } } } }; template T PlusEqualAtomicView(const int64_t input_length) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 2); InitFunctor_Seq init_f(input, length); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); PlusEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T PlusEqualAtomicViewCheck(const int64_t input_length) { const int64_t N = input_length; T result[2]; if (N % 2 == 0) { const int64_t half_sum_end = (N / 2) - 1; const int64_t 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 int64_t half_sum_end = (T)(N / 2); const int64_t 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 bool PlusEqualAtomicViewTest(int64_t input_length) { T res = PlusEqualAtomicView(input_length); T resSerial = PlusEqualAtomicViewCheck(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 struct MinusEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type even_odd_result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. MinusEqualAtomicViewFunctor(const view_type& input_, view_type& even_odd_result_, const int64_t length_) : input(input_), even_odd_result(even_odd_result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % 2 == 0) { even_odd_result(0) -= input(i); } else { even_odd_result(1) -= input(i); } } } }; template T MinusEqualAtomicView(const int64_t input_length) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 2); InitFunctor_Seq init_f(input, length); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); MinusEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T MinusEqualAtomicViewCheck(const int64_t input_length) { const int64_t N = input_length; T result[2]; if (N % 2 == 0) { const int64_t half_sum_end = (N / 2) - 1; const int64_t 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 int64_t half_sum_end = (int64_t)(N / 2); const int64_t 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 bool MinusEqualAtomicViewTest(int64_t input_length) { T res = MinusEqualAtomicView(input_length); T resSerial = MinusEqualAtomicViewCheck(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 struct TimesEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type result; const int64_t length; // Wrap the result view in an atomic view, use this for operator TimesEqualAtomicViewFunctor(const view_type& input_, view_type& result_, const int64_t length_) : input(input_), result(result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length && i > 0) { result(0) *= (double)input(i); } } }; template T TimesEqualAtomicView(const int64_t input_length, const int64_t remainder) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 1); deep_copy(result_view, 1.0); InitFunctor_ModTimes init_f(input, length, remainder); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); TimesEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T TimesEqualAtomicViewCheck(const int64_t input_length, const int64_t remainder) { // Analytical result. const int64_t N = input_length; T result = 1.0; for (int64_t i = 2; i < N; ++i) { if (i % (remainder + 1) == remainder) { result *= 2.0; } else { result *= 1.0; } } return (T)result; } template bool TimesEqualAtomicViewTest(const int64_t input_length) { const int64_t remainder = 23; T res = TimesEqualAtomicView(input_length, remainder); T resSerial = TimesEqualAtomicViewCheck(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 struct DivEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; using scalar_view_type = Kokkos::View; view_type input; atomic_view_type result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. DivEqualAtomicViewFunctor(const view_type& input_, scalar_view_type& result_, const int64_t length_) : input(input_), result(result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length && i > 0) { result() /= (double)(input(i)); } } }; template T DivEqualAtomicView(const int64_t input_length, const int64_t remainder) { using view_type = Kokkos::View; using scalar_view_type = Kokkos::View; using host_scalar_view_type = typename scalar_view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); scalar_view_type result_view("result_view"); Kokkos::deep_copy(result_view, 12121212121); InitFunctor_ModTimes init_f(input, length, remainder); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); DivEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T DivEqualAtomicViewCheck(const int64_t input_length, const int64_t remainder) { const int64_t N = input_length; T result = 12121212121.0; for (int64_t i = 2; i < N; ++i) { if (i % (remainder + 1) == remainder) { result /= 1.0; } else { result /= 2.0; } } return (T)result; } template bool DivEqualAtomicViewTest(const int64_t input_length) { const int64_t remainder = 23; T res = DivEqualAtomicView(input_length, remainder); T resSerial = DivEqualAtomicViewCheck(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 struct ModEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; using scalar_view_type = Kokkos::View; view_type input; atomic_view_type result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. ModEqualAtomicViewFunctor(const view_type& input_, scalar_view_type& result_, const int64_t length_) : input(input_), result(result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length && i > 0) { result() %= (double)(input(i)); } } }; template T ModEqualAtomicView(const int64_t input_length, const int64_t remainder) { using view_type = Kokkos::View; using scalar_view_type = Kokkos::View; using host_scalar_view_type = typename scalar_view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); scalar_view_type result_view("result_view"); Kokkos::deep_copy(result_view, 12121212121); InitFunctor_ModTimes init_f(input, length, remainder); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); ModEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T ModEqualAtomicViewCheck(const int64_t input_length, const int64_t remainder) { const int64_t N = input_length; T result = 12121212121; for (int64_t i = 2; i < N; ++i) { if (i % (remainder + 1) == remainder) { result %= 1; } else { result %= 2; } } return (T)result; } template bool ModEqualAtomicViewTest(const int64_t input_length) { static_assert(std::is_integral::value, "ModEqualAtomicView Error: Type must be integral type for this " "unit test"); const int64_t remainder = 23; T res = ModEqualAtomicView(input_length, remainder); T resSerial = ModEqualAtomicViewCheck(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 struct RSEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; using result_view_type = Kokkos::View; const view_type input; atomic_view_type result; const int64_t length; const int64_t value; // Wrap the result view in an atomic view, use this for operator. RSEqualAtomicViewFunctor(const view_type& input_, result_view_type& result_, const int64_t& length_, const int64_t& value_) : input(input_), result(result_), length(length_), value(value_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t 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 T RSEqualAtomicView(const int64_t input_length, const int64_t value, const int64_t remainder) { using view_type = Kokkos::View; using result_view_type = Kokkos::View; using host_scalar_view_type = typename result_view_type::HostMirror; const int64_t 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 init_f(input, length, remainder); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); RSEqualAtomicViewFunctor functor(input, result_view, length, value); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), functor); Kokkos::fence(); Kokkos::deep_copy(h_result_view, result_view); return (T)(h_result_view(1, 0, 0, 0)); } template T RSEqualAtomicViewCheck(const int64_t input_length, const int64_t value, const int64_t remainder) { T result[4]; result[0] = value; result[1] = value; result[2] = value; result[3] = value; T* input = new T[input_length]; for (int64_t i = 0; i < input_length; ++i) { if (i % (remainder + 1) == remainder) { input[i] = 1; } else { input[i] = 0; } } for (int64_t 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 bool RSEqualAtomicViewTest(const int64_t input_length) { static_assert(std::is_integral::value, "RSEqualAtomicViewTest: Must be integral type for test"); const int64_t remainder = 61042; // prime - 1 const int64_t value = 1073741825; // 2^30+1 T res = RSEqualAtomicView(input_length, value, remainder); T resSerial = RSEqualAtomicViewCheck(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 struct LSEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; using result_view_type = Kokkos::View; view_type input; atomic_view_type result; const int64_t length; const int64_t value; // Wrap the result view in an atomic view, use this for operator. LSEqualAtomicViewFunctor(const view_type& input_, result_view_type& result_, const int64_t& length_, const int64_t& value_) : input(input_), result(result_), length(length_), value(value_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t 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 T LSEqualAtomicView(const int64_t input_length, const int64_t value, const int64_t remainder) { using view_type = Kokkos::View; using result_view_type = Kokkos::View; using host_scalar_view_type = typename result_view_type::HostMirror; const int64_t 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 init_f(input, length, remainder); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); LSEqualAtomicViewFunctor functor(input, result_view, length, value); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), functor); Kokkos::fence(); Kokkos::deep_copy(h_result_view, result_view); return (T)(h_result_view(1, 0, 0, 0)); } template T LSEqualAtomicViewCheck(const int64_t input_length, const int64_t value, const int64_t remainder) { T result[4]; result[0] = value; result[1] = value; result[2] = value; result[3] = value; T* input = new T[input_length]; for (int64_t i = 0; i < input_length; ++i) { if (i % (remainder + 1) == remainder) { input[i] = 1; } else { input[i] = 0; } } for (int64_t 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 bool LSEqualAtomicViewTest(const int64_t input_length) { static_assert(std::is_integral::value, "LSEqualAtomicViewTest: Must be integral type for test"); const int64_t remainder = 61042; // prime - 1 const int64_t value = 1; // 2^30+1 T res = LSEqualAtomicView(input_length, value, remainder); T resSerial = LSEqualAtomicViewCheck(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 struct AndEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type even_odd_result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. AndEqualAtomicViewFunctor(const view_type& input_, view_type& even_odd_result_, const int64_t length_) : input(input_), even_odd_result(even_odd_result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % 2 == 0) { even_odd_result(0) &= input(i); } else { even_odd_result(1) &= input(i); } } } }; template T AndEqualAtomicView(const int64_t input_length) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 2); Kokkos::deep_copy(result_view, 1); InitFunctor_Seq init_f(input, length); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); AndEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T AndEqualAtomicViewCheck(const int64_t input_length) { const int64_t N = input_length; T result[2] = {1}; for (int64_t i = 0; i < N; ++i) { if (N % 2 == 0) { result[0] &= (T)i; } else { result[1] &= (T)i; } } return (result[0]); } template bool AndEqualAtomicViewTest(int64_t input_length) { static_assert(std::is_integral::value, "AndEqualAtomicViewTest: Must be integral type for test"); T res = AndEqualAtomicView(input_length); T resSerial = AndEqualAtomicViewCheck(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 struct OrEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type even_odd_result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. OrEqualAtomicViewFunctor(const view_type& input_, view_type& even_odd_result_, const int64_t length_) : input(input_), even_odd_result(even_odd_result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % 2 == 0) { even_odd_result(0) |= input(i); } else { even_odd_result(1) |= input(i); } } } }; template T OrEqualAtomicView(const int64_t input_length) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 2); InitFunctor_Seq init_f(input, length); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); OrEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T OrEqualAtomicViewCheck(const int64_t input_length) { const int64_t N = input_length; T result[2] = {0}; for (int64_t i = 0; i < N; ++i) { if (i % 2 == 0) { result[0] |= (T)i; } else { result[1] |= (T)i; } } return (T)(result[0]); } template bool OrEqualAtomicViewTest(int64_t input_length) { static_assert(std::is_integral::value, "OrEqualAtomicViewTest: Must be integral type for test"); T res = OrEqualAtomicView(input_length); T resSerial = OrEqualAtomicViewCheck(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 struct XOrEqualAtomicViewFunctor { using atomic_view_type = Kokkos::View >; using view_type = Kokkos::View; view_type input; atomic_view_type even_odd_result; const int64_t length; // Wrap the result view in an atomic view, use this for operator. XOrEqualAtomicViewFunctor(const view_type& input_, view_type& even_odd_result_, const int64_t length_) : input(input_), even_odd_result(even_odd_result_), length(length_) {} KOKKOS_INLINE_FUNCTION void operator()(const int64_t i) const { if (i < length) { if (i % 2 == 0) { even_odd_result(0) ^= input(i); } else { even_odd_result(1) ^= input(i); } } } }; template T XOrEqualAtomicView(const int64_t input_length) { using view_type = Kokkos::View; using host_view_type = typename view_type::HostMirror; const int64_t length = input_length; view_type input("input_view", length); view_type result_view("result_view", 2); InitFunctor_Seq init_f(input, length); Kokkos::parallel_for(Kokkos::RangePolicy(0, length), init_f); XOrEqualAtomicViewFunctor functor(input, result_view, length); Kokkos::parallel_for(Kokkos::RangePolicy(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 T XOrEqualAtomicViewCheck(const int64_t input_length) { const int64_t N = input_length; T result[2] = {0}; for (int64_t i = 0; i < N; ++i) { if (i % 2 == 0) { result[0] ^= (T)i; } else { result[1] ^= (T)i; } } return (T)(result[0]); } template bool XOrEqualAtomicViewTest(int64_t input_length) { static_assert(std::is_integral::value, "XOrEqualAtomicViewTest: Must be integral type for test"); T res = XOrEqualAtomicView(input_length); T resSerial = XOrEqualAtomicViewCheck(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 bool AtomicViewsTestIntegralType(const int length, int test) { static_assert(std::is_integral::value, "TestAtomicViews Error: Non-integral type passed into " "IntegralType tests"); switch (test) { case 1: return PlusEqualAtomicViewTest(length); case 2: return MinusEqualAtomicViewTest(length); case 3: return RSEqualAtomicViewTest(length); case 4: return LSEqualAtomicViewTest(length); case 5: return ModEqualAtomicViewTest(length); case 6: return AndEqualAtomicViewTest(length); case 7: return OrEqualAtomicViewTest(length); case 8: return XOrEqualAtomicViewTest(length); } return 0; } template bool AtomicViewsTestNonIntegralType(const int length, int test) { switch (test) { case 1: return PlusEqualAtomicViewTest(length); case 2: return MinusEqualAtomicViewTest(length); case 3: return TimesEqualAtomicViewTest(length); case 4: return DivEqualAtomicViewTest(length); } return 0; } } // namespace TestAtomicViews namespace Test { TEST(TEST_CATEGORY, atomic_views_integral) { const int64_t length = 1000000; { // Integral Types. ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 1))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 2))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 3))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 4))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 5))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 6))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 7))); ASSERT_TRUE( (TestAtomicViews::AtomicViewsTestIntegralType( length, 8))); } } TEST(TEST_CATEGORY, atomic_views_nonintegral) { const int64_t length = 1000000; { // Non-Integral Types. ASSERT_TRUE(( TestAtomicViews::AtomicViewsTestNonIntegralType( length, 1))); ASSERT_TRUE(( TestAtomicViews::AtomicViewsTestNonIntegralType( length, 2))); ASSERT_TRUE(( TestAtomicViews::AtomicViewsTestNonIntegralType( length, 3))); ASSERT_TRUE(( TestAtomicViews::AtomicViewsTestNonIntegralType( length, 4))); } } TEST(TEST_CATEGORY, atomic_view_api) { TestAtomicViews::TestAtomicViewAPI(); } } // namespace Test