// MIT License // // Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #pragma once #include "primbench.hpp" #include "benchmark_utils.hpp" #include "../common/utils_device_ptr.hpp" #include #include #include #include #include #include #include #include #include template __global__ __launch_bounds__(ROCPRIM_DEFAULT_MAX_BLOCK_SIZE) void warp_reduce_kernel(const T* d_input, T* d_output) { if constexpr(VirtualWaveSize <= rocprim::arch::wavefront::max_size()) { const unsigned int i = blockIdx.x * blockDim.x + threadIdx.x; auto value = d_input[i]; using wreduce_t = rocprim::warp_reduce; __shared__ typename wreduce_t::storage_type storage; ROCPRIM_NO_UNROLL for(unsigned int trial = 0; trial < Trials; ++trial) { wreduce_t().reduce(value, value, storage); } d_output[i] = value; } } template __global__ __launch_bounds__(ROCPRIM_DEFAULT_MAX_BLOCK_SIZE) void segmented_warp_reduce_kernel(const T* d_input, Flag* d_flags, T* d_output) { if constexpr(VirtualWaveSize <= rocprim::arch::wavefront::max_size()) { const unsigned int i = blockIdx.x * blockDim.x + threadIdx.x; auto value = d_input[i]; auto flag = d_flags[i]; using wreduce_t = rocprim::warp_reduce; __shared__ typename wreduce_t::storage_type storage; ROCPRIM_NO_UNROLL for(unsigned int trial = 0; trial < Trials; ++trial) { wreduce_t().head_segmented_reduce(value, value, flag, storage); } d_output[i] = value; } } template struct warp_reduce_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "warp") .add("algo", "warp_reduce") .add("key_type", primbench::name()) .add("broadcast_result", AllReduce) .add("segmented", Segmented) .add("ws", VirtualWaveSize) .add("cfg", primbench::json{}.add("bs", BlockSize)); } void run(primbench::state& state) override { const auto& stream = state.stream; const auto& bytes = state.size; const auto& seed = state.seed; auto seeds = primbench::seeds<2>(seed); using flag_type = unsigned char; size_t N = bytes / sizeof(T); const auto items = BlockSize * ((N + BlockSize - 1) / BlockSize); const auto random_range = limit_random_range(0, 10); std::vector input = get_random_data(items, random_range.first, random_range.second, seeds[0]); std::vector flags = get_random_data(items, 0, 1, seeds[1]); common::device_ptr d_input(input); common::device_ptr d_flags(flags); common::device_ptr d_output(items); state.set_items(items * Trials); state.add_reads(items * Trials); state.run( [&] { if constexpr(Segmented) { segmented_warp_reduce_kernel <<>>(d_input.get(), d_flags.get(), d_output.get()); } else { warp_reduce_kernel <<>>(d_input.get(), d_output.get()); } }); } };