// MIT License // // Copyright (c) 2024-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_data_generation.hpp" #include "../common/utils_device_ptr.hpp" #include #include #include #ifdef BENCHMARK_CONFIG_TUNING #include #endif // BENCHMARK_CONFIG_TUNING #include #include #include #include #ifdef BENCHMARK_CONFIG_TUNING #include #include #endif // BENCHMARK_CONFIG_TUNING enum class partition_probability { p005, p025, p050, p075, tuning }; inline float get_probability(partition_probability probability) { switch(probability) { case partition_probability::p005: return 0.05f; case partition_probability::p025: return 0.25f; case partition_probability::p050: return 0.50f; case partition_probability::p075: return 0.75f; case partition_probability::tuning: return 0.0f; // not used } return 0.0f; } inline const char* get_probability_name(partition_probability probability) { switch(probability) { case partition_probability::p005: return "0.05"; case partition_probability::p025: return "0.25"; case partition_probability::p050: return "0.50"; case partition_probability::p075: return "0.75"; case partition_probability::tuning: return "tuning"; } return "invalid"; } enum class partition_three_way_probability { p005_p025, p025_p050, p050_p075, p075_p100, tuning }; inline std::pair get_probability(partition_three_way_probability probability) { switch(probability) { case partition_three_way_probability::p005_p025: return std::make_pair(0.05f, 0.25f); case partition_three_way_probability::p025_p050: return std::make_pair(0.25f, 0.50f); case partition_three_way_probability::p050_p075: return std::make_pair(0.50f, 0.75f); case partition_three_way_probability::p075_p100: return std::make_pair(0.75f, 1.00f); case partition_three_way_probability::tuning: return std::make_pair(0.00f, 0.00f); // not used } return std::make_pair(0.00f, 0.00f); } inline const char* get_probability_name(partition_three_way_probability probability) { switch(probability) { case partition_three_way_probability::p005_p025: return "0.05:0.25"; case partition_three_way_probability::p025_p050: return "0.25:0.50"; case partition_three_way_probability::p050_p075: return "0.50:0.75"; case partition_three_way_probability::p075_p100: return "0.75:1.00"; case partition_three_way_probability::tuning: return "tuning"; } return "invalid"; } template constexpr auto config_name() { if constexpr(std::is_same_v) { return std::string("default"); } else { auto config = Config(); return primbench::json{} .add("bs", config.kernel_config.block_size) .add("ipt", config.kernel_config.items_per_thread); } } template struct device_partition_flag_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "device") .add("algo", "device_partition") .add("subalgo", "flag") .add("data_type", primbench::name()) .add("flag_type", primbench::name()) .add("probability", get_probability_name(Probability)) .add("cfg", config_name()); } 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); size_t items = bytes / sizeof(DataType); std::vector input = get_random_data(items, common::generate_limits::min(), common::generate_limits::max(), seeds[0]); std::vector flags_0; std::vector flags_1; std::vector flags_2; if(is_tuning) { flags_0 = get_random_data01(items, 0.0f, seeds[1]); flags_1 = get_random_data01(items, 0.5f, seeds[1]); flags_2 = get_random_data01(items, 1.0f, seeds[1]); } else { flags_0 = get_random_data01(items, get_probability(Probability), seeds[1]); } common::device_ptr d_input(input); common::device_ptr d_flags_0(flags_0); common::device_ptr d_flags_1; common::device_ptr d_flags_2; if(is_tuning) { d_flags_1.store(flags_1); d_flags_2.store(flags_2); } common::device_ptr d_output(items); common::device_ptr d_selected_count_output(1); const auto dispatch = [&](void* d_temp_storage, size_t& temp_storage_size_bytes) { const auto dispatch_flags = [&](FlagType* d_flags) { HIP_CHECK(rocprim::partition(d_temp_storage, temp_storage_size_bytes, d_input.get(), d_flags, d_output.get(), d_selected_count_output.get(), items, stream)); }; dispatch_flags(d_flags_0.get()); if(is_tuning) { dispatch_flags(d_flags_1.get()); dispatch_flags(d_flags_2.get()); } }; // Allocate temporary storage memory size_t temp_storage_size_bytes{}; dispatch(nullptr, temp_storage_size_bytes); common::device_ptr d_temp_storage(temp_storage_size_bytes); state.set_items(items); state.add_reads(items); state.run([&] { dispatch(d_temp_storage.get(), temp_storage_size_bytes); }); } private: static constexpr bool is_tuning = Probability == partition_probability::tuning; }; template struct device_partition_predicate_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "device") .add("algo", "device_partition") .add("subalgo", "predicate") .add("data_type", primbench::name()) .add("probability", get_probability_name(Probability)) .add("cfg", config_name()); } void run(primbench::state& state) override { const auto& stream = state.stream; const auto& bytes = state.size; const auto& seed = state.seed; size_t items = bytes / sizeof(DataType); // all data types can represent [0, 127], -1 so a predicate can select all std::vector input = get_random_data(items, static_cast(0), static_cast(126), seed); common::device_ptr d_input(input); common::device_ptr d_output(items); common::device_ptr d_selected_count_output(1); const auto dispatch = [&](void* d_temp_storage, size_t& temp_storage_size_bytes) { const auto dispatch_predicate = [&](float probability) { auto predicate = [probability](const DataType& value) -> bool { return value < static_cast(127 * probability); }; HIP_CHECK(rocprim::partition(d_temp_storage, temp_storage_size_bytes, d_input.get(), d_output.get(), d_selected_count_output.get(), items, predicate, stream)); }; if(is_tuning) { dispatch_predicate(0.0f); dispatch_predicate(0.5f); dispatch_predicate(1.0f); } else { dispatch_predicate(get_probability(Probability)); } }; // Allocate temporary storage memory size_t temp_storage_size_bytes{}; dispatch(nullptr, temp_storage_size_bytes); common::device_ptr d_temp_storage(temp_storage_size_bytes); state.set_items(items); state.add_reads(items); state.run([&] { dispatch(d_temp_storage.get(), temp_storage_size_bytes); }); } private: static constexpr bool is_tuning = Probability == partition_probability::tuning; }; template struct device_partition_two_way_flag_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "device") .add("algo", "device_partition") .add("subalgo", "two_way_flag") .add("data_type", primbench::name()) .add("flag_type", primbench::name()) .add("probability", get_probability_name(Probability)) .add("cfg", config_name()); } 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); size_t items = bytes / sizeof(DataType); std::vector input = get_random_data(items, common::generate_limits::min(), common::generate_limits::max(), seeds[0]); std::vector flags_0; std::vector flags_1; std::vector flags_2; if(is_tuning) { flags_0 = get_random_data01(items, 0.0f, seeds[1]); flags_1 = get_random_data01(items, 0.5f, seeds[1]); flags_2 = get_random_data01(items, 1.0f, seeds[1]); } else { flags_0 = get_random_data01(items, get_probability(Probability), seeds[1]); } common::device_ptr d_input(input); common::device_ptr d_flags_0(flags_0); common::device_ptr d_flags_1; common::device_ptr d_flags_2; if(is_tuning) { d_flags_1.store(flags_1); d_flags_2.store(flags_2); } common::device_ptr d_output_selected(items); common::device_ptr d_output_rejected(items); common::device_ptr d_selected_count_output(1); const auto dispatch = [&](void* d_temp_storage, size_t& temp_storage_size_bytes) { const auto dispatch_flags = [&](FlagType* d_flags) { HIP_CHECK(rocprim::partition_two_way(d_temp_storage, temp_storage_size_bytes, d_input.get(), d_flags, d_output_selected.get(), d_output_rejected.get(), d_selected_count_output.get(), items, stream)); }; dispatch_flags(d_flags_0.get()); if(is_tuning) { dispatch_flags(d_flags_1.get()); dispatch_flags(d_flags_2.get()); } }; // Allocate temporary storage memory size_t temp_storage_size_bytes = 0; dispatch(nullptr, temp_storage_size_bytes); common::device_ptr d_temp_storage(temp_storage_size_bytes); state.set_items(items); state.add_reads(items); state.run([&] { dispatch(d_temp_storage.get(), temp_storage_size_bytes); }); } private: static constexpr bool is_tuning = Probability == partition_probability::tuning; }; template struct device_partition_two_way_predicate_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "device") .add("algo", "device_partition") .add("subalgo", "two_way_predicate") .add("data_type", primbench::name()) .add("probability", get_probability_name(Probability)) .add("cfg", config_name()); } void run(primbench::state& state) override { const auto& stream = state.stream; const auto& bytes = state.size; const auto& seed = state.seed; size_t items = bytes / sizeof(DataType); // all data types can represent [0, 127], -1 so a predicate can select all std::vector input = get_random_data(items, static_cast(0), static_cast(126), seed); common::device_ptr d_input(input); common::device_ptr d_output_selected(items); common::device_ptr d_output_rejected(items); common::device_ptr d_selected_count_output(1); const auto dispatch = [&](void* d_temp_storage, size_t& temp_storage_size_bytes) { const auto dispatch_predicate = [&](float probability) { auto predicate = [probability](const DataType& value) -> bool { return value < static_cast(127 * probability); }; HIP_CHECK(rocprim::partition_two_way(d_temp_storage, temp_storage_size_bytes, d_input.get(), d_output_selected.get(), d_output_rejected.get(), d_selected_count_output.get(), items, predicate, stream)); }; if(is_tuning) { dispatch_predicate(0.0f); dispatch_predicate(0.5f); dispatch_predicate(1.0f); } else { dispatch_predicate(get_probability(Probability)); } }; // Allocate temporary storage memory size_t temp_storage_size_bytes{}; dispatch(nullptr, temp_storage_size_bytes); common::device_ptr d_temp_storage(temp_storage_size_bytes); state.set_items(items); state.add_reads(items); state.run([&] { dispatch(d_temp_storage.get(), temp_storage_size_bytes); }); } private: static constexpr bool is_tuning = Probability == partition_probability::tuning; }; template struct device_partition_three_way_benchmark : public primbench::benchmark_interface { primbench::json meta() const override { return primbench::json{} .add("lvl", "device") .add("algo", "device_partition") .add("subalgo", "three_way") .add("data_type", primbench::name()) .add("probability", get_probability_name(Probability)) .add("cfg", config_name()); } void run(primbench::state& state) override { const auto& stream = state.stream; const auto& bytes = state.size; const auto& seed = state.seed; size_t items = bytes / sizeof(DataType); // all data types can represent [0, 127], -1 so a predicate can select all std::vector input = get_random_data(items, static_cast(0), static_cast(126), seed); common::device_ptr d_input(input); common::device_ptr d_output_first(items); common::device_ptr d_output_second(items); common::device_ptr d_output_unselected(items); common::device_ptr d_selected_count_output(2); const auto dispatch = [&](void* d_temp_storage, size_t& temp_storage_size_bytes) { const auto dispatch_predicate = [&](std::pair probability) { const float probability_one = probability.first; auto predicate_one = [probability_one](const DataType& value) { return value < DataType(127 * probability_one); }; const float probability_two = probability.second; auto predicate_two = [probability_two](const DataType& value) { return value < DataType(127 * probability_two); }; HIP_CHECK(rocprim::partition_three_way(d_temp_storage, temp_storage_size_bytes, d_input.get(), d_output_first.get(), d_output_second.get(), d_output_unselected.get(), d_selected_count_output.get(), items, predicate_one, predicate_two, stream)); }; if(is_tuning) { // clang-format off std::array, 7> probabilities = {{ {0.33f, 0.66f}, // 1st, 2nd, and 3rd bin {0.50f, 1.00f}, // 1st and 2nd bin {0.00f, 0.50f}, // 2nd and 3rd bin {0.50f, 0.50f}, // 1st and 3rd bin {1.00f, 1.00f}, // 1st bin {0.00f, 1.00f}, // 2nd bin {0.00f, 0.00f}}}; // 3rd bin // clang-format on for(const std::pair& probability : probabilities) { dispatch_predicate(probability); } } else { dispatch_predicate(get_probability(Probability)); } }; // Allocate temporary storage memory size_t temp_storage_size_bytes{}; dispatch(nullptr, temp_storage_size_bytes); common::device_ptr d_temp_storage(temp_storage_size_bytes); state.set_items(items); state.add_reads(items); state.run([&] { dispatch(d_temp_storage.get(), temp_storage_size_bytes); }); } private: static constexpr bool is_tuning = Probability == partition_three_way_probability::tuning; }; #ifdef BENCHMARK_CONFIG_TUNING template struct device_partition_benchmark_generator { template struct create_ipt { void operator()(std::vector>& storage) { using config = rocprim::select_config; storage.emplace_back( std::make_unique>()); storage.emplace_back( std::make_unique>()); storage.emplace_back( std::make_unique>()); storage.emplace_back( std::make_unique>()); storage.emplace_back( std::make_unique>()); } }; static void create(std::vector>& storage) { static constexpr int max_items_per_thread = std::min(64 / sizeof(DataType), size_t{32}); static_for_each, create_ipt>(storage); } }; #endif // BENCHMARK_CONFIG_TUNING