/* * Copyright 2008-2013 NVIDIA Corporation * Modifications Copyright© 2024-2025 Advanced Micro Devices, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "bitwise_repro/bwr_utils.hpp" #include "test_param_fixtures.hpp" #include "test_real_assertions.hpp" #include "test_utils.hpp" using ReproducibilityTestParams = ::testing::Types, std::decay_t>, Params, std::decay_t>, Params, std::decay_t>, Params, std::decay_t>, Params, std::decay_t>, Params, std::decay_t>>; TESTS_DEFINE(ReproducibilityTests, ReproducibilityTestParams); // Delay the operator by a semi-random amount to increase the likelyhood // of changing the number of lookback steps between the runs. template struct eepy_scan_op { bool enable_sleep; F scan_op; eepy_scan_op(bool enable_sleep, F scan_op = F()) : enable_sleep(enable_sleep) , scan_op(scan_op) {} template __device__ auto operator()(const T& a, const U& b) -> decltype(scan_op(a, b)) { if (this->enable_sleep) { for (unsigned int i = 0; i < blockIdx.x * 3001 % 64; ++i) { __builtin_amdgcn_s_sleep(63); } } return scan_op(a, b); } }; template void assert_reproducible(const thrust::device_vector& d_a, const thrust::device_vector& d_b) { thrust::host_vector h_a = d_a; thrust::host_vector h_b = d_b; ASSERT_NO_FATAL_FAILURE(assert_bit_eq(h_a, h_b)); } void check_bwr_match(const bwr_utils::TokenHelper& token_helper) { if (inter_run_bwr::enabled && inter_run_bwr::db) { ASSERT_TRUE(inter_run_bwr::db->match(token_helper.get_input_token(), token_helper.get_output_token())); } } TYPED_TEST(ReproducibilityTests, Scan) { using Vector = typename TestFixture::input_type; using Policy = typename TestFixture::execution_policy; using T = typename Vector::value_type; using ScanOp = eepy_scan_op>; bwr_utils::TokenHelper token_helper; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); Policy policy; for (auto size : get_sizes()) { SCOPED_TRACE(testing::Message() << "with size= " << size); for (auto seed : get_seeds()) { SCOPED_TRACE(testing::Message() << "with seed= " << seed); thrust::host_vector h_input = get_random_data(size, static_cast(-100), static_cast(100), seed); Vector d_input = h_input; Vector d_output_0(size); Vector d_output_1(size); // inclusive thrust::inclusive_scan(policy, d_input.begin(), d_input.end(), d_output_0.begin(), ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_input; token_helper.build_input_token( "thrust::inclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_functor_token("thrust::plus")}); } thrust::inclusive_scan(policy, d_input.begin(), d_input.end(), d_output_1.begin(), ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); // exclusive thrust::exclusive_scan(policy, d_input.begin(), d_input.end(), d_output_0.begin(), T{42}, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_input; token_helper.build_input_token( "thrust::exclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_scalar_token(T{42}), bwr_utils::get_functor_token("thrust::plus")}); } thrust::exclusive_scan(policy, d_input.begin(), d_input.end(), d_output_1.begin(), T{42}, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); } } } TYPED_TEST(ReproducibilityTests, ScanByKey) { using Vector = typename TestFixture::input_type; using Policy = typename TestFixture::execution_policy; using T = typename Vector::value_type; using ScanOp = eepy_scan_op>; hipDeviceProp_t attributes; HIP_CHECK(hipGetDeviceProperties(&attributes, 0)); // Disable int test case for Navi3X because of known issue if (std::is_same_v && attributes.major == 11) { GTEST_SKIP(); } bwr_utils::TokenHelper token_helper; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); Policy policy; for (auto size : get_sizes()) { SCOPED_TRACE(testing::Message() << "with size= " << size); for (auto seed : get_seeds()) { SCOPED_TRACE(testing::Message() << "with seed= " << seed); thrust::host_vector h_keys(size); thrust::default_random_engine rng(seed); const auto r = static_cast(std::sqrt(size)); for (size_t i = 0, k = 0, l = 0; i < size; i++) { if (l == 0) { l = 1 + rng() % r; ++k; } --l; h_keys[i] = k; } thrust::device_vector d_keys = h_keys; thrust::host_vector h_input = get_random_data(size, static_cast(-100), static_cast(100), seed); Vector d_input = h_input; Vector d_output_0(size); Vector d_output_1(size); // inclusive thrust::inclusive_scan_by_key( policy, d_keys.begin(), d_keys.end(), d_input.begin(), d_output_0.begin(), thrust::equal_to{}, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_keys = d_keys; thrust::host_vector h_input = d_input; token_helper.build_input_token( "thrust::inclusive_scan_by_key", h_keys.begin(), h_keys.end(), h_input.begin(), {bwr_utils::get_functor_token("thrust::equal_to"), bwr_utils::get_functor_token("thrust::plus")}); } thrust::inclusive_scan_by_key( policy, d_keys.begin(), d_keys.end(), d_input.begin(), d_output_1.begin(), thrust::equal_to{}, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); // exclusive thrust::exclusive_scan_by_key( policy, d_keys.begin(), d_keys.end(), d_input.begin(), d_output_0.begin(), T{123}, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_keys = d_keys; thrust::host_vector h_input = d_input; token_helper.build_input_token( "thrust::exclusive_scan_by_key", h_keys.begin(), h_keys.end(), h_input.begin(), {bwr_utils::get_scalar_token(T{123}), bwr_utils::get_functor_token("thrust::plus")}); } thrust::exclusive_scan_by_key( policy, d_keys.begin(), d_keys.end(), d_input.begin(), d_output_1.begin(), T{123}, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); } } } TYPED_TEST(ReproducibilityTests, ReduceByKey) { using Vector = typename TestFixture::input_type; using Policy = typename TestFixture::execution_policy; using T = typename Vector::value_type; using ScanOp = eepy_scan_op>; bwr_utils::TokenHelper token_helper; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); Policy policy; for (auto size : get_sizes()) { SCOPED_TRACE(testing::Message() << "with size= " << size); for (auto seed : get_seeds()) { SCOPED_TRACE(testing::Message() << "with seed= " << seed); thrust::host_vector h_keys(size); thrust::default_random_engine rng(seed); const auto r = static_cast(std::sqrt(size)); for (size_t i = 0, k = 0, l = 0; i < size; i++) { if (l == 0) { l = 1 + rng() % r; ++k; } --l; h_keys[i] = k; } thrust::device_vector d_keys = h_keys; thrust::host_vector h_vals = get_random_data(size, static_cast(-100), static_cast(100), seed); Vector d_vals = h_vals; Vector d_keys_output_0(size); Vector d_vals_output_0(size); Vector d_keys_output_1(size); Vector d_vals_output_1(size); thrust::reduce_by_key( policy, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys_output_0.begin(), d_vals_output_0.begin(), thrust::equal_to{}, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_keys = d_keys; thrust::host_vector h_vals = d_vals; token_helper.build_input_token( "thrust::reduce_by_key", h_keys.begin(), h_keys.end(), h_vals.begin(), {bwr_utils::get_functor_token("thrust::equal_to"), bwr_utils::get_functor_token("thrust::plus")}); } thrust::reduce_by_key( policy, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys_output_1.begin(), d_vals_output_1.begin(), thrust::equal_to{}, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_keys_output_1; token_helper.build_output_token(h_data.begin(), h_data.begin(), d_keys.size()); check_bwr_match(token_helper); } assert_reproducible(d_keys_output_0, d_keys_output_1); assert_reproducible(d_vals_output_0, d_vals_output_1); } } } TYPED_TEST(ReproducibilityTests, TransformScan) { using Vector = typename TestFixture::input_type; using Policy = typename TestFixture::execution_policy; using T = typename Vector::value_type; using ScanOp = eepy_scan_op>; bwr_utils::TokenHelper token_helper; SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest()); Policy policy; for (auto size : get_sizes()) { SCOPED_TRACE(testing::Message() << "with size= " << size); for (auto seed : get_seeds()) { SCOPED_TRACE(testing::Message() << "with seed= " << seed); thrust::host_vector h_input = get_random_data(size, get_default_limits::min(), get_default_limits::max(), seed); Vector d_input = h_input; Vector d_output_0(size); Vector d_output_1(size); thrust::transform_inclusive_scan( policy, d_input.begin(), d_input.end(), d_output_0.begin(), thrust::negate(), ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_input; token_helper.build_input_token( "thrust::transform_inclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_functor_token("thrust::negate"), bwr_utils::get_functor_token("thrust::plus")}); } thrust::transform_inclusive_scan( policy, d_input.begin(), d_input.end(), d_output_1.begin(), thrust::negate(), ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); thrust::transform_exclusive_scan( policy, d_input.begin(), d_input.end(), d_output_0.begin(), thrust::negate(), (T) 11, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_input; token_helper.build_input_token( "thrust::transform_exclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_functor_token("thrust::negate"), bwr_utils::get_scalar_token((T) 11), bwr_utils::get_functor_token("thrust::plus")}); } thrust::transform_exclusive_scan( policy, d_input.begin(), d_input.end(), d_output_1.begin(), thrust::negate(), (T) 11, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); // in-place scans d_output_0 = d_input; d_output_1 = d_input; thrust::transform_inclusive_scan( policy, d_output_0.begin(), d_output_0.end(), d_output_0.begin(), thrust::negate(), ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_input_token( "thrust::transform_inclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_functor_token("thrust::negate"), bwr_utils::get_functor_token("thrust::plus")}); } thrust::transform_inclusive_scan( policy, d_output_1.begin(), d_output_1.end(), d_output_1.begin(), thrust::negate(), ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); d_output_0 = d_input; d_output_1 = d_input; thrust::transform_exclusive_scan( policy, d_output_0.begin(), d_output_0.end(), d_output_0.begin(), thrust::negate(), (T) 11, ScanOp(false)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_input_token( "thrust::transform_exclusive_scan", h_data.begin(), h_data.end(), {bwr_utils::get_functor_token("thrust::negate"), bwr_utils::get_scalar_token((T) 11), bwr_utils::get_functor_token("thrust::plus")}); } thrust::transform_exclusive_scan( policy, d_output_1.begin(), d_output_1.end(), d_output_1.begin(), thrust::negate(), (T) 11, ScanOp(true)); if (inter_run_bwr::enabled) { thrust::host_vector h_data = d_output_1; token_helper.build_output_token(h_data.begin(), h_data.size()); check_bwr_match(token_helper); } assert_reproducible(d_output_0, d_output_1); } } }