//===----------------------------------------------------------------------===// // // Part of CUDA Experimental in CUDA C++ Core Libraries, // under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr int cluster_size = 2; constexpr int block_size = 128; /*********************************************************************************************************************** * Thread Reduce Wrapper Kernels **********************************************************************************************************************/ template struct ReduceKernel { template __device__ void operator()( Config config, cuda::std::integral_constant, const T* __restrict__ d_in, T* __restrict__ d_out, RedOp red_op) { cudax::this_grid grid{config}; T thread_data[NumItems]; for (int i = 0; i < NumItems; ++i) { thread_data[i] = d_in[cuda::gpu_thread.rank_as(grid) + i * cuda::gpu_thread.count_as(grid)]; } if constexpr (Broadcasted) { const auto result = cudax::coop::reduce(cudax::broadcasted, grid, thread_data, red_op); d_out[cuda::gpu_thread.rank(grid)] = result; } else { const auto result = cudax::coop::reduce(grid, thread_data, red_op); REQUIRE(result.has_value() == cuda::gpu_thread.is_root_rank(grid)); if (cuda::gpu_thread.is_root_rank(grid)) { *d_out = result.value(); } } } }; /*********************************************************************************************************************** * Type list definition **********************************************************************************************************************/ using integral_type_list = c2h::type_list; using fp_type_list = c2h::type_list; using operator_integral_list = c2h::type_list, cuda::std::multiplies<>, cuda::std::bit_and<>, cuda::std::bit_or<>, cuda::std::bit_xor<>, cuda::minimum<>, cuda::maximum<>>; using operator_fp_list = c2h::type_list, cuda::std::multiplies<>, cuda::minimum<>, cuda::maximum<>>; using grid_size_list = c2h::enum_type_list; /*********************************************************************************************************************** * Verify results and kernel launch` **********************************************************************************************************************/ template void verify_results(const T& expected_data, const T& test_results) { if constexpr (cuda::std::is_floating_point_v) { REQUIRE_THAT(expected_data, Catch::Matchers::WithinRel(test_results, T{0.05})); } else { REQUIRE(expected_data == test_results); } } template void run_reduce_kernel( cuda::stream_ref stream, cuda::std::integral_constant, int num_items, const c2h::device_vector& in, c2h::device_vector& out, RedOp red_op, cuda::std::bool_constant = {}) { const auto config = cuda::make_config( cuda::grid_dims(), cuda::cluster_dims(), cuda::block_dims(), cuda::cooperative_launch{}); const auto in_ptr = thrust::raw_pointer_cast(in.data()); const auto out_ptr = thrust::raw_pointer_cast(out.data()); const ReduceKernel kernel{}; switch (num_items) { case 1: cuda::launch(stream, config, kernel, cuda::std::integral_constant{}, in_ptr, out_ptr, red_op); break; case 4: cuda::launch(stream, config, kernel, cuda::std::integral_constant{}, in_ptr, out_ptr, red_op); break; default: FAIL("Unsupported number of items"); } stream.sync(); } constexpr int max_size = 4; constexpr int num_seeds = 10; /*********************************************************************************************************************** * Test cases **********************************************************************************************************************/ _CCCL_DIAG_SUPPRESS_MSVC(4244) // warning C4244: '=': conversion from 'int' to '_Tp', possible loss of data C2H_TEST("reduce/this_grid Integral Type Tests", "[reduce][this_grid]", integral_type_list, operator_integral_list, grid_size_list) { const auto device = cuda::devices[0]; if (cuda::device_attributes::compute_capability_major(device) < 9) { return; } using value_t = c2h::get<0, TestType>; using op_t = c2h::get<1, TestType>; using grid_size_t = c2h::get<2, TestType>; constexpr auto reduce_op = op_t{}; constexpr auto operator_identity = cuda::identity_element(); CAPTURE(c2h::type_name(), max_size, c2h::type_name()); c2h::device_vector d_in(max_size * grid_size_t::value * cluster_size * block_size); c2h::device_vector d_out(1); c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits::min()); c2h::host_vector h_in = d_in; cuda::stream stream{device}; for (int num_items : {1, 4}) { auto reference_result = cuda::std::accumulate( h_in.begin(), h_in.begin() + num_items * grid_size_t::value * cluster_size * block_size, operator_identity, reduce_op); run_reduce_kernel(stream, grid_size_t{}, num_items, d_in, d_out, reduce_op); verify_results(reference_result, c2h::host_vector(d_out)[0]); } } C2H_TEST( "reduce/this_grid Floating-Point Type Tests", "[reduce][this_grid]", fp_type_list, operator_fp_list, grid_size_list) { const auto device = cuda::devices[0]; if (cuda::device_attributes::compute_capability_major(device) < 9) { return; } using value_t = c2h::get<0, TestType>; using op_t = c2h::get<1, TestType>; using grid_size_t = c2h::get<2, TestType>; constexpr auto reduce_op = op_t{}; const auto operator_identity = cuda::identity_element(); CAPTURE(c2h::type_name(), max_size, c2h::type_name()); c2h::device_vector d_in(max_size * grid_size_t::value * cluster_size * block_size); c2h::device_vector d_out(1); c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits::min()); c2h::host_vector h_in = d_in; cuda::stream stream{device}; for (int num_items : {1, 4}) { auto reference_result = cuda::std::accumulate( h_in.begin(), h_in.begin() + num_items * grid_size_t::value * cluster_size * block_size, operator_identity, reduce_op); run_reduce_kernel(stream, grid_size_t{}, num_items, d_in, d_out, reduce_op); verify_results(reference_result, c2h::host_vector(d_out)[0]); } } C2H_TEST("reduce/this_grid Broadcasted", "[reduce][this_grid]", integral_type_list, grid_size_list) { const auto device = cuda::devices[0]; if (cuda::device_attributes::compute_capability_major(device) < 9) { return; } using value_t = c2h::get<0, TestType>; using op_t = cuda::std::plus<>; using grid_size_t = c2h::get<1, TestType>; constexpr auto reduce_op = op_t{}; constexpr auto operator_identity = cuda::identity_element(); CAPTURE(c2h::type_name(), max_size, c2h::type_name()); c2h::device_vector d_in(max_size * grid_size_t::value * cluster_size * block_size); c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits::min()); c2h::host_vector h_in = d_in; cuda::stream stream{device}; for (int num_items : {1, 4}) { c2h::device_vector d_out(grid_size_t::value * cluster_size * block_size); auto reference_result = cuda::std::accumulate( h_in.begin(), h_in.begin() + num_items * grid_size_t::value * cluster_size * block_size, operator_identity, reduce_op); run_reduce_kernel(stream, grid_size_t{}, num_items, d_in, d_out, reduce_op, cuda::std::true_type{}); verify_results(c2h::host_vector(grid_size_t::value * cluster_size * block_size, reference_result), c2h::host_vector(d_out)); } }