Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1328 lines
56 KiB
C++

// MIT License
//
// Copyright (c) 2017-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.
#include "common_test_header.hpp"
// hipcub API
#include <hipcub/device/device_select.hpp>
#include <hipcub/iterator/counting_input_iterator.hpp>
#include <hipcub/iterator/discard_output_iterator.hpp>
#include "single_index_iterator.hpp"
#include "test_utils_bfloat16.hpp"
#include "test_utils_data_generation.hpp"
#include "test_utils_half.hpp"
// Params for tests
template<class InputType,
class OutputType = InputType,
class FlagType = unsigned int,
bool UseGraphs = false>
struct DeviceSelectParams
{
using input_type = InputType;
using output_type = OutputType;
using flag_type = FlagType;
static constexpr bool use_graphs = UseGraphs;
};
template<class Params>
class HipcubDeviceSelectTests : public ::testing::Test
{
public:
using input_type = typename Params::input_type;
using output_type = typename Params::output_type;
using flag_type = typename Params::flag_type;
static constexpr bool use_graphs = Params::use_graphs;
};
using HipcubDeviceSelectTestsParams
= ::testing::Types<DeviceSelectParams<int, long>,
DeviceSelectParams<float, float>,
DeviceSelectParams<unsigned char, float>,
DeviceSelectParams<test_utils::half, test_utils::half>,
DeviceSelectParams<test_utils::bfloat16, test_utils::bfloat16>,
DeviceSelectParams<int, long, unsigned int, true>>;
TYPED_TEST_SUITE(HipcubDeviceSelectTests, HipcubDeviceSelectTestsParams);
TYPED_TEST(HipcubDeviceSelectTests, Flagged)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = typename TestFixture::input_type;
using U = typename TestFixture::output_type;
using F = typename TestFixture::flag_type;
constexpr bool inplace = std::is_same<T, U>::value;
hipStream_t stream = 0; // default
if(TestFixture::use_graphs)
{
// Default stream does not support hipGraph stream capture, so create one
HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));
}
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; seed_index++)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
// Generate data
std::vector<T> input
= test_utils::get_random_data<T>(size,
test_utils::convert_to_device<T>(1),
test_utils::convert_to_device<T>(100),
seed_value);
std::vector<F> flags = test_utils::get_random_data<F>(size,
static_cast<F>(0),
static_cast<F>(1),
seed_value + seed_value_addition);
T* d_input;
F* d_flags;
U* d_output;
unsigned int* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_input, input.size() * sizeof(T)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_flags, flags.size() * sizeof(F)));
if HIPCUB_IF_CONSTEXPR(!inplace)
{
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output, input.size() * sizeof(U)));
}
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_selected_count_output, sizeof(unsigned int)));
HIP_CHECK(
hipMemcpy(d_input, input.data(), input.size() * sizeof(T), hipMemcpyHostToDevice));
HIP_CHECK(
hipMemcpy(d_flags, flags.data(), flags.size() * sizeof(F), hipMemcpyHostToDevice));
// Calculate expected results on host
std::vector<U> expected;
expected.reserve(input.size());
for(size_t i = 0; i < input.size(); i++)
{
if(flags[i] != 0)
{
expected.push_back(input[i]);
}
}
auto call = [&](void* d_temp_storage, size_t& temp_storage_size_bytes)
{
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipcub::DeviceSelect::Flagged(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_flags,
d_selected_count_output,
input.size(),
stream));
}
else
{
HIP_CHECK(hipcub::DeviceSelect::Flagged(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_flags,
d_output,
d_selected_count_output,
input.size(),
stream));
}
};
// temp storage
size_t temp_storage_size_bytes;
void* d_temp_storage = nullptr;
// Get size of d_temp_storage
call(d_temp_storage, temp_storage_size_bytes);
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
test_utils::GraphHelper gHelper;
if (TestFixture::use_graphs)
gHelper.startStreamCapture(stream);
// Run
call(d_temp_storage, temp_storage_size_bytes);
if (TestFixture::use_graphs)
gHelper.createAndLaunchGraph(stream);
HIP_CHECK(hipDeviceSynchronize());
// Check if number of selected value is as expected
unsigned int selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, expected.size());
// Check if output values are as expected
std::vector<U> output(input.size());
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipMemcpy(output.data(),
d_input,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
else
{
HIP_CHECK(hipMemcpy(output.data(),
d_output,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
ASSERT_NO_FATAL_FAILURE(test_utils::assert_eq(output, expected, expected.size()));
if(TestFixture::use_graphs)
gHelper.cleanupGraphHelper();
HIP_CHECK(hipFree(d_input));
HIP_CHECK(hipFree(d_flags));
if(!inplace)
{
HIP_CHECK(hipFree(d_output));
}
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
if(TestFixture::use_graphs)
HIP_CHECK(hipStreamDestroy(stream));
}
TEST(HipcubDeviceSelectTests, FlagNormalization)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = int;
using U = long;
using F = unsigned int;
hipStream_t stream = 0; // default stream
unsigned int seed_value = rand();
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
rocprim::counting_iterator<T> d_input(0);
rocprim::counting_iterator<F> d_flags(1);
U* d_output;
unsigned int* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output, size * sizeof(*d_output)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_selected_count_output,
sizeof(*d_selected_count_output)));
// Calculate expected results on host
std::vector<U> expected;
expected.reserve(size);
for(size_t i = 0; i < size; i++)
{
if(d_flags[i] != 0)
{
expected.push_back(static_cast<U>(i));
}
}
// temp storage
size_t temp_storage_size_bytes = 0;
// Get size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::Flagged(nullptr,
temp_storage_size_bytes,
d_input,
d_flags,
d_output,
d_selected_count_output,
size,
stream));
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
void* d_temp_storage = nullptr;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
// Run
HIP_CHECK(hipcub::DeviceSelect::Flagged(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_flags,
d_output,
d_selected_count_output,
size,
stream));
// Check if number of selected value is as expected
unsigned int selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, size);
// Check if output values are as expected
std::vector<U> output(size);
HIP_CHECK(
hipMemcpy(output.data(), d_output, size * sizeof(*d_output), hipMemcpyDeviceToHost));
ASSERT_NO_FATAL_FAILURE(test_utils::assert_eq(output, expected));
HIP_CHECK(hipFree(d_output));
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
struct TestSelectOp
{
template<class T>
__host__ __device__
inline bool
operator()(const T& value) const
{
if(value > T(50))
return true;
return false;
}
};
TYPED_TEST(HipcubDeviceSelectTests, SelectOp)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = typename TestFixture::input_type;
using U = typename TestFixture::output_type;
constexpr bool inplace = std::is_same<T, U>::value;
hipStream_t stream = 0; // default
if(TestFixture::use_graphs)
{
// Default stream does not support hipGraph stream capture, so create one
HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));
}
TestSelectOp select_op;
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; seed_index++)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
// Generate data
std::vector<T> input
= test_utils::get_random_data<T>(size,
test_utils::convert_to_device<T>(0),
test_utils::convert_to_device<T>(100),
seed_value);
T* d_input;
U* d_output;
unsigned int* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_input, input.size() * sizeof(T)));
if HIPCUB_IF_CONSTEXPR(!inplace)
{
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output, input.size() * sizeof(U)));
}
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_selected_count_output, sizeof(unsigned int)));
HIP_CHECK(
hipMemcpy(d_input, input.data(), input.size() * sizeof(T), hipMemcpyHostToDevice));
// Calculate expected results on host
std::vector<U> expected;
expected.reserve(input.size());
for(size_t i = 0; i < input.size(); i++)
{
if(select_op(input[i]))
{
expected.push_back(input[i]);
}
}
auto call = [&](void* d_temp_storage, size_t& temp_storage_size_bytes)
{
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipcub::DeviceSelect::If(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_selected_count_output,
input.size(),
select_op,
stream));
}
else
{
HIP_CHECK(hipcub::DeviceSelect::If(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
input.size(),
select_op,
stream));
}
};
// temp storage
size_t temp_storage_size_bytes;
void* d_temp_storage = nullptr;
// Get size of d_temp_storage
call(d_temp_storage, temp_storage_size_bytes);
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
test_utils::GraphHelper gHelper;
if (TestFixture::use_graphs)
gHelper.startStreamCapture(stream);
// Run
call(d_temp_storage, temp_storage_size_bytes);
if (TestFixture::use_graphs)
gHelper.createAndLaunchGraph(stream);
HIP_CHECK(hipDeviceSynchronize());
// Check if number of selected value is as expected
unsigned int selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, expected.size());
// Check if output values are as expected
std::vector<U> output(input.size());
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipMemcpy(output.data(),
d_input,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
else
{
HIP_CHECK(hipMemcpy(output.data(),
d_output,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
ASSERT_NO_FATAL_FAILURE(test_utils::assert_eq(output, expected, expected.size()));
if(TestFixture::use_graphs)
gHelper.cleanupGraphHelper();
HIP_CHECK(hipFree(d_input));
if(!inplace)
{
HIP_CHECK(hipFree(d_output));
}
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
if(TestFixture::use_graphs)
HIP_CHECK(hipStreamDestroy(stream));
}
TYPED_TEST(HipcubDeviceSelectTests, FlaggedIf)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = typename TestFixture::input_type;
using U = typename TestFixture::output_type;
using F = typename TestFixture::flag_type;
constexpr bool inplace = std::is_same<T, U>::value;
hipStream_t stream = 0; // default
if(TestFixture::use_graphs)
{
// Default stream does not support hipGraph stream capture, so create one
HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));
}
TestSelectOp select_flag_op;
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; seed_index++)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
// Generate data
std::vector<T> input
= test_utils::get_random_data<T>(size,
test_utils::convert_to_device<T>(1),
test_utils::convert_to_device<T>(100),
seed_value);
std::vector<F> flags = test_utils::get_random_data<F>(size,
static_cast<F>(0),
static_cast<F>(100),
seed_value + seed_value_addition);
T* d_input;
F* d_flags;
U* d_output;
unsigned int* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_input, input.size() * sizeof(T)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_flags, flags.size() * sizeof(F)));
if HIPCUB_IF_CONSTEXPR(!inplace)
{
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output, input.size() * sizeof(U)));
}
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_selected_count_output, sizeof(unsigned int)));
HIP_CHECK(
hipMemcpy(d_input, input.data(), input.size() * sizeof(T), hipMemcpyHostToDevice));
HIP_CHECK(
hipMemcpy(d_flags, flags.data(), flags.size() * sizeof(F), hipMemcpyHostToDevice));
// Calculate expected results on host
std::vector<U> expected;
expected.reserve(input.size());
for(size_t i = 0; i < input.size(); i++)
{
if(select_flag_op(flags[i]))
{
expected.push_back(input[i]);
}
}
auto call = [&](void* d_temp_storage, size_t& temp_storage_size_bytes)
{
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipcub::DeviceSelect::FlaggedIf(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_flags,
d_selected_count_output,
input.size(),
select_flag_op,
stream));
}
else
{
HIP_CHECK(hipcub::DeviceSelect::FlaggedIf(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_flags,
d_output,
d_selected_count_output,
input.size(),
select_flag_op,
stream));
}
};
// temp storage
size_t temp_storage_size_bytes;
void* d_temp_storage = nullptr;
// Get size of d_temp_storage
call(d_temp_storage, temp_storage_size_bytes);
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
test_utils::GraphHelper gHelper;
if (TestFixture::use_graphs)
gHelper.startStreamCapture(stream);
// Run
call(d_temp_storage, temp_storage_size_bytes);
if (TestFixture::use_graphs)
gHelper.createAndLaunchGraph(stream);
HIP_CHECK(hipDeviceSynchronize());
// Check if number of selected value is as expected
unsigned int selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, expected.size());
// Check if output values are as expected
std::vector<U> output(input.size());
if HIPCUB_IF_CONSTEXPR(inplace)
{
HIP_CHECK(hipMemcpy(output.data(),
d_input,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
else
{
HIP_CHECK(hipMemcpy(output.data(),
d_output,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
}
ASSERT_NO_FATAL_FAILURE(test_utils::assert_eq(output, expected, expected.size()));
if(TestFixture::use_graphs)
gHelper.cleanupGraphHelper();
HIP_CHECK(hipFree(d_input));
HIP_CHECK(hipFree(d_flags));
if(!inplace)
{
HIP_CHECK(hipFree(d_output));
}
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
if(TestFixture::use_graphs)
HIP_CHECK(hipStreamDestroy(stream));
}
std::vector<float> get_discontinuity_probabilities()
{
std::vector<float> probabilities = {0.0001, 0.05, 0.25, 0.5, 0.75, 0.95};
return probabilities;
}
TYPED_TEST(HipcubDeviceSelectTests, Unique)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = typename TestFixture::input_type;
using U = typename TestFixture::output_type;
hipStream_t stream = 0; // default
if(TestFixture::use_graphs)
{
// Default stream does not support hipGraph stream capture, so create one
HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));
}
const auto probabilities = get_discontinuity_probabilities();
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; seed_index++)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
for(auto p : probabilities)
{
SCOPED_TRACE(testing::Message() << "with p= " << p);
// Generate data
std::vector<T> input(size);
{
std::vector<T> input01 = test_utils::get_random_data01<T>(size, p, seed_value);
test_utils::host_inclusive_scan(input01.begin(),
input01.end(),
input.begin(),
hipcub::Sum());
}
// Allocate and copy to device
T* d_input;
U* d_output;
unsigned int* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_input, input.size() * sizeof(T)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output, input.size() * sizeof(U)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_selected_count_output,
sizeof(unsigned int)));
HIP_CHECK(hipMemcpy(d_input,
input.data(),
input.size() * sizeof(T),
hipMemcpyHostToDevice));
HIP_CHECK(hipDeviceSynchronize());
// Calculate expected results on host
std::vector<U> expected;
expected.reserve(input.size());
expected.push_back(input[0]);
for(size_t i = 1; i < input.size(); i++)
{
if(!(test_utils::convert_to_native(input[i - 1])
== test_utils::convert_to_native(input[i])))
{
expected.push_back(input[i]);
}
}
// temp storage
size_t temp_storage_size_bytes;
// Get size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::Unique(nullptr,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
input.size(),
stream));
HIP_CHECK(hipDeviceSynchronize());
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
void* d_temp_storage = nullptr;
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
HIP_CHECK(hipDeviceSynchronize());
test_utils::GraphHelper gHelper;
if (TestFixture::use_graphs)
gHelper.startStreamCapture(stream);
// Run
HIP_CHECK(hipcub::DeviceSelect::Unique(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
input.size(),
stream));
if (TestFixture::use_graphs)
gHelper.createAndLaunchGraph(stream);
HIP_CHECK(hipDeviceSynchronize());
// Check if number of selected value is as expected
unsigned int selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(unsigned int),
hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
ASSERT_EQ(selected_count_output, expected.size());
// Check if output values are as expected
std::vector<U> output(input.size());
HIP_CHECK(hipMemcpy(output.data(),
d_output,
output.size() * sizeof(U),
hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
ASSERT_NO_FATAL_FAILURE(test_utils::assert_eq(output, expected, expected.size()));
if(TestFixture::use_graphs)
gHelper.cleanupGraphHelper();
HIP_CHECK(hipFree(d_input));
HIP_CHECK(hipFree(d_output));
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
}
if(TestFixture::use_graphs)
HIP_CHECK(hipStreamDestroy(stream));
}
TEST(HipcubDeviceSelectTests, UniqueDiscardOutputIterator)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
hipStream_t stream = 0; //default stream
unsigned int seed_value = rand();
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
rocprim::counting_iterator<unsigned int> d_input(0);
rocprim::discard_iterator d_output;
size_t* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper((&d_selected_count_output), sizeof(size_t)));
// temp storage
size_t temp_storage_size_bytes = 0;
// Get size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::Unique(nullptr,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
size,
stream));
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0U);
// allocate temporary storage
void* d_temp_storage = nullptr;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
// Run
HIP_CHECK(hipcub::DeviceSelect::Unique(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
size,
stream));
// Check if number of selected value is as expected
size_t selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(size_t),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, size);
HIP_CHECK(hipFree(d_temp_storage));
HIP_CHECK(hipFree(d_selected_count_output));
}
}
template<class T>
struct TestLargeIndicesSelectOp
{
T max_value;
__host__ __device__
inline bool
operator()(const T& value) const
{
return test_utils::less()(value, T(max_value));
}
};
class HipcubDeviceSelectLargeIndicesTests : public ::testing::TestWithParam<unsigned int>
{
public:
const bool debug_synchronous = false;
};
INSTANTIATE_TEST_SUITE_P(HipcubDeviceSelectLargeIndicesTest,
HipcubDeviceSelectLargeIndicesTests,
::testing::Values(2048, 9643, 32768, 38713, 38713));
TEST_P(HipcubDeviceSelectLargeIndicesTests, LargeIndicesSelectOp)
{
GTEST_SKIP_ASAN();
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using T = size_t; // input_type
using U = size_t; // output_type
using selected_count_type = size_t;
hipStream_t stream = 0; // default stream
const auto selected_size = GetParam();
for(size_t size : test_utils::get_large_sizes(0))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
// Support for large indices in DeviceSelect is not implemented in CUB yet. Disable test meanwhile.
#ifdef __HIP_PLATFORM_NVIDIA__
std::cout << "Test disabled for large sizes until support is present in CUB" << std::endl;
GTEST_SKIP();
#endif
// Generate data
rocprim::counting_iterator<T> d_input(0);
U* d_output;
selected_count_type* d_selected_count_output;
selected_count_type expected_output_size = selected_size;
TestLargeIndicesSelectOp<T> select_op{expected_output_size};
HIP_CHECK(test_common_utils::hipMallocHelper(&d_output,
sizeof(d_output[0]) * expected_output_size));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_selected_count_output,
sizeof(d_selected_count_output[0])));
// Calculate expected results on host
std::vector<U> expected_output(expected_output_size);
std::iota(expected_output.begin(), expected_output.end(), U(0));
// Temp storage
size_t temp_storage_size_bytes;
void* d_temp_storage = nullptr;
// Get the size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::If(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
size,
select_op,
stream));
HIP_CHECK(hipDeviceSynchronize());
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0);
// Allocate temporary storage
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
// Run
HIP_CHECK(hipcub::DeviceSelect::If(d_temp_storage,
temp_storage_size_bytes,
d_input,
d_output,
d_selected_count_output,
size,
select_op,
stream));
HIP_CHECK(hipDeviceSynchronize());
// Check if number of selected value is as expected
selected_count_type selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(*d_selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, selected_size);
// Check if outputs are as expected
std::vector<U> output(expected_output_size);
HIP_CHECK(hipMemcpy(output.data(),
d_output,
sizeof(*d_output) * expected_output_size,
hipMemcpyDeviceToHost));
ASSERT_NO_FATAL_FAILURE(
test_utils::assert_eq(output, expected_output, expected_output_size));
HIP_CHECK(hipFree(d_output));
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
template<typename KeyType,
typename ValueType,
typename EqualityOp = hipcub::Equality,
typename OutputKeyType = KeyType,
typename OutputValueType = ValueType,
typename SelectedCountType = unsigned int,
bool UseGraphs = false>
struct DeviceUniqueByKeyParams
{
using key_type = KeyType;
using value_type = ValueType;
using equality_op = EqualityOp;
using output_key_type = OutputKeyType;
using output_value_type = OutputValueType;
using selected_count_type = SelectedCountType;
static constexpr bool use_graphs = UseGraphs;
};
template<class Params>
class HipcubDeviceUniqueByKeyTests : public ::testing::Test
{
public:
using key_type = typename Params::key_type;
using value_type = typename Params::value_type;
using equality_op = typename Params::equality_op;
using output_key_type = typename Params::output_key_type;
using output_value_type = typename Params::output_value_type;
using selected_count_type = typename Params::selected_count_type;
static constexpr bool use_graphs = Params::use_graphs;
};
struct TestUniqueEqualityOp
{
static constexpr int segment = 3;
template<typename T>
HIPCUB_HOST_DEVICE
bool operator()(const T& a, const T& b) const
{
static_assert(std::is_integral<T>::value, "T must be integral type");
return a / segment == b / segment;
}
};
using HipcubDeviceUniqueByKeyTestsParams = ::testing::Types<
DeviceUniqueByKeyParams<int, int>,
DeviceUniqueByKeyParams<size_t, int, TestUniqueEqualityOp, double, long long, size_t>,
DeviceUniqueByKeyParams<double, float, hipcub::Equality, double, double, size_t>,
DeviceUniqueByKeyParams<uint8_t, long long>,
DeviceUniqueByKeyParams<test_utils::custom_test_type<double>,
test_utils::custom_test_type<int>>,
DeviceUniqueByKeyParams<int, int, hipcub::Equality, int, int, unsigned int, true>>;
TYPED_TEST_SUITE(HipcubDeviceUniqueByKeyTests, HipcubDeviceUniqueByKeyTestsParams);
TYPED_TEST(HipcubDeviceUniqueByKeyTests, UniqueByKey)
{
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using key_type = typename TestFixture::key_type;
using value_type = typename TestFixture::value_type;
using output_key_type = typename TestFixture::output_key_type;
using output_value_type = typename TestFixture::output_value_type;
using selected_count_type = typename TestFixture::selected_count_type;
hipStream_t stream = 0; // default
if(TestFixture::use_graphs)
{
// Default stream does not support hipGraph stream capture, so create one
HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));
}
const auto probabilities = get_discontinuity_probabilities();
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; seed_index++)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
for(auto p : probabilities)
{
SCOPED_TRACE(testing::Message() << "with p= " << p);
// Generate data
std::vector<key_type> input_keys(size);
{
std::vector<key_type> input01
= test_utils::get_random_data01<key_type>(size, p, seed_value);
test_utils::host_inclusive_scan(input01.begin(),
input01.end(),
input_keys.begin(),
hipcub::Sum());
}
const auto input_values
= test_utils::get_random_data<value_type>(size, -1000, 1000, seed_value);
// Allocate and copy to device
key_type* d_keys_input;
value_type* d_values_input;
output_key_type* d_keys_output;
output_value_type* d_values_output;
selected_count_type* d_selected_count_output;
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_keys_input,
input_keys.size() * sizeof(*d_keys_input)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_values_input,
input_values.size()
* sizeof(*d_values_input)));
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_keys_output,
input_keys.size() * sizeof(*d_keys_output)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_values_output,
input_values.size()
* sizeof(*d_values_output)));
HIP_CHECK(test_common_utils::hipMallocHelper(&d_selected_count_output,
sizeof(*d_selected_count_output)));
HIP_CHECK(hipMemcpy(d_keys_input,
input_keys.data(),
input_keys.size() * sizeof(input_keys[0]),
hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_values_input,
input_values.data(),
input_values.size() * sizeof(input_values[0]),
hipMemcpyHostToDevice));
// Caclulate expected result on host
std::vector<output_key_type> expected_keys;
std::vector<output_value_type> expected_values;
expected_keys.reserve(input_keys.size());
expected_values.reserve(input_values.size());
expected_keys.push_back(input_keys[0]);
expected_values.push_back(input_values[0]);
typename TestFixture::equality_op equality_op;
for(size_t i = 1; i < input_keys.size(); ++i)
{
if(!equality_op(input_keys[i - 1], input_keys[i]))
{
expected_keys.push_back(input_keys[i]);
expected_values.push_back(input_values[i]);
}
}
// temp storage
size_t temp_storage_size_bytes;
// Get the size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::UniqueByKey(nullptr,
temp_storage_size_bytes,
d_keys_input,
d_values_input,
d_keys_output,
d_values_output,
d_selected_count_output,
input_keys.size(),
equality_op,
stream));
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0);
// allocate temporary storage
void* d_temp_storage = nullptr;
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
test_utils::GraphHelper gHelper;
if (TestFixture::use_graphs)
gHelper.startStreamCapture(stream);
// run
HIP_CHECK(hipcub::DeviceSelect::UniqueByKey(d_temp_storage,
temp_storage_size_bytes,
d_keys_input,
d_values_input,
d_keys_output,
d_values_output,
d_selected_count_output,
input_keys.size(),
equality_op,
stream));
if (TestFixture::use_graphs)
gHelper.createAndLaunchGraph(stream);
// Check if number of selected value is as expected
selected_count_type selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, expected_keys.size());
// Check if outputs are as expected
std::vector<output_key_type> output_keys(input_keys.size());
HIP_CHECK(hipMemcpy(output_keys.data(),
d_keys_output,
output_keys.size() * sizeof(output_keys[0]),
hipMemcpyDeviceToHost));
std::vector<output_value_type> output_values(input_values.size());
HIP_CHECK(hipMemcpy(output_values.data(),
d_values_output,
output_values.size() * sizeof(output_values[0]),
hipMemcpyDeviceToHost));
ASSERT_NO_FATAL_FAILURE(
test_utils::assert_eq(output_keys, expected_keys, expected_keys.size()));
ASSERT_NO_FATAL_FAILURE(
test_utils::assert_eq(output_values, expected_values, expected_values.size()));
if(TestFixture::use_graphs)
gHelper.cleanupGraphHelper();
HIP_CHECK(hipFree(d_keys_input));
HIP_CHECK(hipFree(d_values_input));
HIP_CHECK(hipFree(d_keys_output));
HIP_CHECK(hipFree(d_values_output));
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
}
if(TestFixture::use_graphs)
HIP_CHECK(hipStreamDestroy(stream));
}
TEST(HipcubDeviceUniqueByKeyTests, LargeIndicesUniqueByKey)
{
GTEST_SKIP_ASAN();
int device_id = test_common_utils::obtain_device_from_ctest();
SCOPED_TRACE(testing::Message() << "with device_id= " << device_id);
HIP_CHECK(hipSetDevice(device_id));
using key_type = size_t;
using value_type = size_t;
using selected_count_type = size_t;
hipStream_t stream = 0; // default stream
for(size_t seed_index = 0; seed_index < random_seeds_count + seed_size; ++seed_index)
{
unsigned int seed_value
= seed_index < random_seeds_count ? rand() : seeds[seed_index - random_seeds_count];
SCOPED_TRACE(testing::Message() << "with seed= " << seed_value);
for(size_t size : test_utils::get_large_sizes(seed_value))
{
SCOPED_TRACE(testing::Message() << "with size= " << size);
TestUniqueEqualityOp equality_op;
const size_t selected_count
= (size + TestUniqueEqualityOp::segment - 1) / TestUniqueEqualityOp::segment;
const size_t output_index = selected_count - 1;
const size_t input_index = output_index * TestUniqueEqualityOp::segment;
rocprim::counting_iterator<key_type> d_keys_input(0);
rocprim::counting_iterator<value_type> d_values_input(123);
key_type* d_keys_output;
value_type* d_values_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_keys_output, sizeof(*d_keys_output)));
HIP_CHECK(
test_common_utils::hipMallocHelper(&d_values_output, sizeof(*d_values_output)));
test_utils::single_index_iterator<key_type> keys_output_it(d_keys_output, output_index);
test_utils::single_index_iterator<value_type> values_output_it(d_values_output,
output_index);
selected_count_type* d_selected_count_output;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_selected_count_output,
sizeof(*d_selected_count_output)));
// temp storage
size_t temp_storage_size_bytes;
// Get the size of d_temp_storage
HIP_CHECK(hipcub::DeviceSelect::UniqueByKey(nullptr,
temp_storage_size_bytes,
d_keys_input,
d_values_input,
keys_output_it,
values_output_it,
d_selected_count_output,
size,
equality_op,
stream));
// temp_storage_size_bytes must be >0
ASSERT_GT(temp_storage_size_bytes, 0);
// allocate temporary storage
void* d_temp_storage = nullptr;
HIP_CHECK(test_common_utils::hipMallocHelper(&d_temp_storage, temp_storage_size_bytes));
// run
HIP_CHECK(hipcub::DeviceSelect::UniqueByKey(d_temp_storage,
temp_storage_size_bytes,
d_keys_input,
d_values_input,
keys_output_it,
values_output_it,
d_selected_count_output,
size,
equality_op,
stream));
// Check if number of selected value is as expected
selected_count_type selected_count_output = 0;
HIP_CHECK(hipMemcpy(&selected_count_output,
d_selected_count_output,
sizeof(*d_selected_count_output),
hipMemcpyDeviceToHost));
ASSERT_EQ(selected_count_output, selected_count);
// Check if outputs are as expected
key_type output_key;
value_type output_value;
HIP_CHECK(hipMemcpy(&output_key,
d_keys_output,
sizeof(*d_keys_output),
hipMemcpyDeviceToHost));
HIP_CHECK(hipMemcpy(&output_value,
d_values_output,
sizeof(*d_values_output),
hipMemcpyDeviceToHost));
const key_type expected_key = d_keys_input[input_index];
const value_type expected_value = d_values_input[input_index];
ASSERT_EQ(output_key, expected_key);
ASSERT_EQ(output_value, expected_value);
HIP_CHECK(hipFree(d_keys_output));
HIP_CHECK(hipFree(d_values_output));
HIP_CHECK(hipFree(d_selected_count_output));
HIP_CHECK(hipFree(d_temp_storage));
}
}
}