Files
TheRock/rocm-libraries/projects/rocthrust/test/test_scan.cpp
T
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

990 lines
31 KiB
C++

/*
* Copyright 2008-2013 NVIDIA Corporation
* Modifications Copyright© 2019-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 <thrust/detail/config.h>
#include <thrust/device_free.h>
#include <thrust/device_malloc.h>
#include <thrust/functional.h>
#include <thrust/iterator/constant_iterator.h>
#include <thrust/iterator/discard_iterator.h>
#include <thrust/iterator/retag.h>
#include <thrust/scan.h>
#include "test_param_fixtures.hpp"
#include "test_real_assertions.hpp"
#include "test_utils.hpp"
#include _THRUST_STD_INCLUDE(array)
TESTS_DEFINE(ScanTests, FullTestsParams);
TESTS_DEFINE(ScanVariablesTests, NumericalTestsParams);
TESTS_DEFINE(ScanVectorTests, VectorSignedIntegerTestsParams);
using MixedParams = ::testing::Types<Params<std::tuple<thrust::host_vector<int>, thrust::host_vector<float>>>,
Params<std::tuple<thrust::device_vector<int>, thrust::device_vector<float>>>>;
TESTS_DEFINE(ScanMixedTests, MixedParams);
template <typename T>
struct max_functor
{
THRUST_HOST_DEVICE T operator()(T rhs, T lhs) const
{
return thrust::max(rhs, lhs);
}
};
TYPED_TEST(ScanVectorTests, TestScanSimple)
{
using Vector = typename TestFixture::input_type;
using T = typename Vector::value_type;
typename Vector::iterator iter;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
Vector input(5);
Vector result(5);
Vector output(5);
input = {1, 3, -2, 4, -5};
Vector input_copy(input);
// inclusive scan
iter = thrust::inclusive_scan(input.begin(), input.end(), output.begin());
result = {1, 4, 2, 6, 1};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// exclusive scan
iter = thrust::exclusive_scan(input.begin(), input.end(), output.begin(), T(0));
result = {0, 1, 4, 2, 6};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// exclusive scan with init
iter = thrust::exclusive_scan(input.begin(), input.end(), output.begin(), T(3));
result = {3, 4, 7, 5, 9};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// inclusive scan with op
iter = thrust::inclusive_scan(input.begin(), input.end(), output.begin(), thrust::plus<T>());
result = {1, 4, 2, 6, 1};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// inclusive scan with init and op
iter = thrust::inclusive_scan(input.begin(), input.end(), output.begin(), T(-1), thrust::multiplies<T>());
result = {-1, -3, 6, 24, -120};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// exclusive scan with init and op
iter = thrust::exclusive_scan(input.begin(), input.end(), output.begin(), T(3), thrust::plus<T>());
result = {3, 4, 7, 5, 9};
ASSERT_EQ(std::size_t(iter - output.begin()), input.size());
ASSERT_EQ(input, input_copy);
ASSERT_EQ(output, result);
// inplace inclusive scan
input = input_copy;
iter = thrust::inclusive_scan(input.begin(), input.end(), input.begin());
result = {1, 4, 2, 6, 1};
ASSERT_EQ(std::size_t(iter - input.begin()), input.size());
ASSERT_EQ(input, result);
// inplace inclusive scan with init and op
input = input_copy;
iter = thrust::inclusive_scan(input.begin(), input.end(), input.begin(), T(3), thrust::plus<T>());
result = {4, 7, 5, 9, 4};
ASSERT_EQ(std::size_t(iter - input.begin()), input.size());
ASSERT_EQ(input, result);
// inplace exclusive scan with init
input = input_copy;
iter = thrust::exclusive_scan(input.begin(), input.end(), input.begin(), T(3));
result = {3, 4, 7, 5, 9};
ASSERT_EQ(std::size_t(iter - input.begin()), input.size());
ASSERT_EQ(input, result);
// inplace exclusive scan with implicit init=0
input = input_copy;
iter = thrust::exclusive_scan(input.begin(), input.end(), input.begin());
result = {0, 1, 4, 2, 6};
ASSERT_EQ(std::size_t(iter - input.begin()), input.size());
ASSERT_EQ(input, result);
}
template <typename InputIterator, typename OutputIterator>
OutputIterator inclusive_scan(my_system& system, InputIterator, InputIterator, OutputIterator result)
{
system.validate_dispatch();
return result;
}
TEST(ScanTests, TestInclusiveScanDispatchExplicit)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
thrust::device_vector<int> vec(1);
my_system sys(0);
thrust::inclusive_scan(sys, vec.begin(), vec.begin(), vec.begin());
ASSERT_EQ(true, sys.is_valid());
}
template <typename InputIterator, typename OutputIterator>
OutputIterator inclusive_scan(my_tag, InputIterator, InputIterator, OutputIterator result)
{
*result = 13;
return result;
}
TEST(ScanTests, TestInclusiveScanDispatchImplicit)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
thrust::device_vector<int> vec(1);
thrust::inclusive_scan(
thrust::retag<my_tag>(vec.begin()), thrust::retag<my_tag>(vec.begin()), thrust::retag<my_tag>(vec.begin()));
ASSERT_EQ(13, vec.front());
}
template <typename InputIterator, typename OutputIterator>
OutputIterator exclusive_scan(my_system& system, InputIterator, InputIterator, OutputIterator result)
{
system.validate_dispatch();
return result;
}
TEST(ScanTests, TestExclusiveScanDispatchExplicit)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
thrust::device_vector<int> vec(1);
my_system sys(0);
thrust::exclusive_scan(sys, vec.begin(), vec.begin(), vec.begin());
ASSERT_EQ(true, sys.is_valid());
}
template <typename InputIterator, typename OutputIterator>
OutputIterator exclusive_scan(my_tag, InputIterator, InputIterator, OutputIterator result)
{
*result = 13;
return result;
}
TEST(ScanTests, TestExclusiveScanDispatchImplicit)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
thrust::device_vector<int> vec(1);
thrust::exclusive_scan(
thrust::retag<my_tag>(vec.begin()), thrust::retag<my_tag>(vec.begin()), thrust::retag<my_tag>(vec.begin()));
ASSERT_EQ(13, vec.front());
}
TEST(ScanTests, TestInclusiveScan32)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
using T = int;
size_t n = 32;
for (auto seed : get_seeds())
{
SCOPED_TRACE(testing::Message() << "with seed= " << seed);
thrust::host_vector<T> h_input =
get_random_data<T>(n, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::host_vector<T> h_output(n);
thrust::device_vector<T> d_output(n);
thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());
thrust::inclusive_scan(d_input.begin(), d_input.end(), d_output.begin());
ASSERT_EQ(d_output, h_output);
}
}
TEST(ScanTests, TestExclusiveScan32)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
using T = int;
size_t n = 32;
T init = 13;
for (auto seed : get_seeds())
{
SCOPED_TRACE(testing::Message() << "with seed= " << seed);
thrust::host_vector<T> h_input =
get_random_data<T>(n, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::host_vector<T> h_output(n);
thrust::device_vector<T> d_output(n);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), init);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_output.begin(), init);
ASSERT_EQ(d_output, h_output);
}
}
template <class IntVector, class FloatVector>
void TestScanMixedTypes()
{
// make sure we get types for default args and operators correct
IntVector int_input{1, 2, 3, 4};
FloatVector float_input{1.5, 2.5, 3.5, 4.5};
IntVector int_output(4);
FloatVector float_output(4);
// float -> int should use plus<void> operator and float accumulator by default
thrust::inclusive_scan(float_input.begin(), float_input.end(), int_output.begin());
ASSERT_EQ(int_output[0], 1); // in: 1.5 accum: 1.5f out: 1
ASSERT_EQ(int_output[1], 4); // in: 2.5 accum: 4.0f out: 4
ASSERT_EQ(int_output[2], 7); // in: 3.5 accum: 7.5f out: 7
ASSERT_EQ(int_output[3], 12); // in: 4.5 accum: 12.f out: 12
// float -> float with plus<int> operator (float accumulator)
thrust::inclusive_scan(float_input.begin(), float_input.end(), float_output.begin(), thrust::plus<int>());
ASSERT_EQ(float_output[0], 1.5f); // in: 1.5 accum: 1.5f out: 1.5f
ASSERT_EQ(float_output[1], 3.0f); // in: 2.5 accum: 3.0f out: 3.0f
ASSERT_EQ(float_output[2], 6.0f); // in: 3.5 accum: 6.0f out: 6.0f
ASSERT_EQ(float_output[3], 10.0f); // in: 4.5 accum: 10.f out: 10.f
// float -> int should use plus<void> operator and float accumulator by default
thrust::exclusive_scan(float_input.begin(), float_input.end(), int_output.begin());
ASSERT_EQ(int_output[0], 0); // out: 0.0f in: 1.5 accum: 1.5f
ASSERT_EQ(int_output[1], 1); // out: 1.5f in: 2.5 accum: 4.0f
ASSERT_EQ(int_output[2], 4); // out: 4.0f in: 3.5 accum: 7.5f
ASSERT_EQ(int_output[3], 7); // out: 7.5f in: 4.5 accum: 12.f
// float -> int should use plus<> operator and float accumulator by default
thrust::exclusive_scan(float_input.begin(), float_input.end(), int_output.begin(), (float) 5.5);
ASSERT_EQ(int_output[0], 5); // out: 5.5f in: 1.5 accum: 7.0f
ASSERT_EQ(int_output[1], 7); // out: 7.0f in: 2.5 accum: 9.5f
ASSERT_EQ(int_output[2], 9); // out: 9.5f in: 3.5 accum: 13.0f
ASSERT_EQ(int_output[3], 13); // out: 13.f in: 4.5 accum: 17.4f
// int -> float should use using plus<> operator and int accumulator by default
thrust::inclusive_scan(int_input.begin(), int_input.end(), float_output.begin());
ASSERT_EQ(float_output[0], 1.f); // in: 1 accum: 1 out: 1
ASSERT_EQ(float_output[1], 3.f); // in: 2 accum: 3 out: 3
ASSERT_EQ(float_output[2], 6.f); // in: 3 accum: 6 out: 6
ASSERT_EQ(float_output[3], 10.f); // in: 4 accum: 10 out: 10
// int -> float + float init_value should use using plus<> operator and
// float accumulator by default
thrust::exclusive_scan(int_input.begin(), int_input.end(), float_output.begin(), (float) 5.5);
ASSERT_EQ(float_output[0], 5.5f); // out: 5.5f in: 1 accum: 6.5f
ASSERT_EQ(float_output[1], 6.5f); // out: 6.0f in: 2 accum: 8.5f
ASSERT_EQ(float_output[2], 8.5f); // out: 8.0f in: 3 accum: 11.5f
ASSERT_EQ(float_output[3], 11.5f); // out: 11.f in: 4 accum: 15.5f
}
TEST(ScanTests, TestScanMixedTypesHost)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
TestScanMixedTypes<thrust::host_vector<int>, thrust::host_vector<float>>();
}
TEST(ScanTests, TestScanMixedTypesDevice)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
TestScanMixedTypes<thrust::device_vector<int>, thrust::device_vector<float>>();
}
TYPED_TEST(ScanVariablesTests, TestScanWithOperator)
{
using T = typename TestFixture::input_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<T> h_input =
get_random_data<T>(size, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::host_vector<T> h_output(size);
thrust::device_vector<T> d_output(size);
thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), max_functor<T>());
thrust::inclusive_scan(d_input.begin(), d_input.end(), d_output.begin(), max_functor<T>());
ASSERT_EQ(d_output, h_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), T(13), max_functor<T>());
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_output.begin(), T(13), max_functor<T>());
ASSERT_EQ(d_output, h_output);
}
}
}
TYPED_TEST(ScanVariablesTests, TestScanWithOperatorToDiscardIterator)
{
using T = typename TestFixture::input_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<T> h_input =
get_random_data<T>(size, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::discard_iterator<> reference(size);
thrust::discard_iterator<> h_result =
thrust::inclusive_scan(h_input.begin(), h_input.end(), thrust::make_discard_iterator(), max_functor<T>());
thrust::discard_iterator<> d_result =
thrust::inclusive_scan(d_input.begin(), d_input.end(), thrust::make_discard_iterator(), max_functor<T>());
ASSERT_EQ_QUIET(reference, h_result);
ASSERT_EQ_QUIET(reference, d_result);
h_result = thrust::exclusive_scan(
h_input.begin(), h_input.end(), thrust::make_discard_iterator(), T(13), max_functor<T>());
d_result = thrust::exclusive_scan(
d_input.begin(), d_input.end(), thrust::make_discard_iterator(), T(13), max_functor<T>());
ASSERT_EQ_QUIET(reference, h_result);
ASSERT_EQ_QUIET(reference, d_result);
}
}
}
TYPED_TEST(ScanVariablesTests, TestScan)
{
using T = typename TestFixture::input_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<T> h_input =
get_random_data<T>(size, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::host_vector<T> h_output(size);
thrust::device_vector<T> d_output(size);
thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());
thrust::inclusive_scan(d_input.begin(), d_input.end(), d_output.begin());
test_equality_scan(h_output, d_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin());
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_output.begin());
test_equality_scan(h_output, d_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_output.begin(), (T) 11);
test_equality_scan(h_output, d_output);
// in-place scans
h_output = h_input;
d_output = d_input;
thrust::inclusive_scan(h_output.begin(), h_output.end(), h_output.begin());
thrust::inclusive_scan(d_output.begin(), d_output.end(), d_output.begin());
test_equality_scan(h_output, d_output);
h_output = h_input;
d_output = d_input;
thrust::exclusive_scan(h_output.begin(), h_output.end(), h_output.begin());
thrust::exclusive_scan(d_output.begin(), d_output.end(), d_output.begin());
test_equality_scan(h_output, d_output);
}
}
}
TYPED_TEST(ScanVariablesTests, TestScanToDiscardIterator)
{
using T = typename TestFixture::input_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<T> h_input =
get_random_data<T>(size, get_default_limits<T>::min(), get_default_limits<T>::max(), seed);
thrust::device_vector<T> d_input = h_input;
thrust::discard_iterator<> h_result =
thrust::inclusive_scan(h_input.begin(), h_input.end(), thrust::make_discard_iterator());
thrust::discard_iterator<> d_result =
thrust::inclusive_scan(d_input.begin(), d_input.end(), thrust::make_discard_iterator());
thrust::discard_iterator<> reference(size);
ASSERT_EQ_QUIET(reference, h_result);
ASSERT_EQ_QUIET(reference, d_result);
h_result = thrust::exclusive_scan(h_input.begin(), h_input.end(), thrust::make_discard_iterator(), (T) 11);
d_result = thrust::exclusive_scan(d_input.begin(), d_input.end(), thrust::make_discard_iterator(), (T) 11);
ASSERT_EQ_QUIET(reference, h_result);
ASSERT_EQ_QUIET(reference, d_result);
}
}
}
TEST(ScanTests, TestScanMixedTypes)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
const unsigned int n = 113;
for (auto seed : get_seeds())
{
SCOPED_TRACE(testing::Message() << "with seed= " << seed);
thrust::host_vector<unsigned int> h_input = get_random_data<unsigned int>(
n, get_default_limits<unsigned int>::min(), get_default_limits<unsigned int>::max(), seed);
for (size_t i = 0; i < n; i++)
{
h_input[i] %= 10;
}
thrust::device_vector<unsigned int> d_input = h_input;
thrust::host_vector<float> h_float_output(n);
thrust::device_vector<float> d_float_output(n);
thrust::host_vector<int> h_int_output(n);
thrust::device_vector<int> d_int_output(n);
// mixed input/output types
thrust::inclusive_scan(h_input.begin(), h_input.end(), h_float_output.begin());
thrust::inclusive_scan(d_input.begin(), d_input.end(), d_float_output.begin());
ASSERT_EQ(d_float_output, h_float_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_float_output.begin(), (float) 3.5);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_float_output.begin(), (float) 3.5);
ASSERT_EQ(d_float_output, h_float_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_float_output.begin(), (int) 3);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_float_output.begin(), (int) 3);
ASSERT_EQ(d_float_output, h_float_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_int_output.begin(), (int) 3);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_int_output.begin(), (int) 3);
ASSERT_EQ(d_int_output, h_int_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_int_output.begin(), (float) 3.5);
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_int_output.begin(), (float) 3.5);
ASSERT_EQ(d_int_output, h_int_output);
}
}
template <typename T, unsigned int N>
void _TestScanWithLargeTypes()
{
size_t n = (1024 * 1024) / sizeof(FixedVector<T, N>);
thrust::host_vector<FixedVector<T, N>> h_input(n);
thrust::host_vector<FixedVector<T, N>> h_output(n);
for (size_t i = 0; i < h_input.size(); i++)
{
h_input[i] = FixedVector<T, N>(static_cast<T>(i));
}
thrust::device_vector<FixedVector<T, N>> d_input = h_input;
thrust::device_vector<FixedVector<T, N>> d_output(n);
thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());
thrust::inclusive_scan(d_input.begin(), d_input.end(), d_output.begin());
ASSERT_EQ_QUIET(h_output, d_output);
thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), FixedVector<T, N>(0));
thrust::exclusive_scan(d_input.begin(), d_input.end(), d_output.begin(), FixedVector<T, N>(0));
ASSERT_EQ_QUIET(h_output, d_output);
}
TEST(ScanTests, TestScanWithLargeTypes)
{
#ifdef ADDRESS_SANITIZER_BUILD
GTEST_SKIP() << "Skipping test due to memory constraints in address sanitizer build.";
#endif
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
_TestScanWithLargeTypes<int, 1>();
#if !defined(__QNX__)
_TestScanWithLargeTypes<int, 8>();
_TestScanWithLargeTypes<int, 64>();
#else
// KNOWN_FAILURE;
#endif
}
template <typename T>
struct plus_mod3
{
T* table;
plus_mod3(T* table)
: table(table)
{}
THRUST_HOST_DEVICE T operator()(T a, T b)
{
return table[(int) (a + b)];
}
};
TYPED_TEST(ScanVectorTests, TestInclusiveScanWithIndirection)
{
// add numbers modulo 3 with external lookup table
using Vector = typename TestFixture::input_type;
using T = typename Vector::value_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
Vector data{0, 1, 2, 1, 2, 0, 1};
Vector table{0, 1, 2, 0, 1, 2};
thrust::inclusive_scan(data.begin(), data.end(), data.begin(), plus_mod3<T>(thrust::raw_pointer_cast(&table[0])));
ASSERT_EQ(data, (Vector{0, 1, 0, 1, 0, 0, 1}));
}
__global__ THRUST_HIP_LAUNCH_BOUNDS_DEFAULT void InclusiveScanKernel(int const N, int* in_array, int* out_array)
{
if (threadIdx.x == 0)
{
thrust::device_ptr<int> in_begin(in_array);
thrust::device_ptr<int> in_end(in_array + N);
thrust::device_ptr<int> out_begin(out_array);
thrust::inclusive_scan(thrust::hip::par, in_begin, in_end, out_begin);
}
}
TEST(ScanTests, TestInclusiveScanDevice)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<int> h_data = get_random_data<int>(size, 0, size, seed);
thrust::host_vector<int> h_output(size);
thrust::inclusive_scan(h_data.begin(), h_data.end(), h_output.begin());
thrust::device_vector<int> d_data = h_data;
thrust::device_vector<int> d_output(size);
hipLaunchKernelGGL(
InclusiveScanKernel,
dim3(1, 1, 1),
dim3(128, 1, 1),
0,
0,
size,
thrust::raw_pointer_cast(&d_data[0]),
thrust::raw_pointer_cast(&d_output[0]));
HIP_CHECK(hipGetLastError());
ASSERT_EQ(h_output, d_output);
}
}
}
__global__ THRUST_HIP_LAUNCH_BOUNDS_DEFAULT void ExclusiveScanKernel(int const N, int* in_array, int* out_array)
{
if (threadIdx.x == 0)
{
thrust::device_ptr<int> in_begin(in_array);
thrust::device_ptr<int> in_end(in_array + N);
thrust::device_ptr<int> out_begin(out_array);
thrust::exclusive_scan(thrust::hip::par, in_begin, in_end, out_begin);
}
}
TEST(ScanTests, TestExclusiveScanDevice)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
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<int> h_data = get_random_data<int>(size, 0, size, seed);
thrust::host_vector<int> h_output(size);
thrust::exclusive_scan(h_data.begin(), h_data.end(), h_output.begin());
thrust::device_vector<int> d_data = h_data;
thrust::device_vector<int> d_output(size);
hipLaunchKernelGGL(
ExclusiveScanKernel,
dim3(1, 1, 1),
dim3(128, 1, 1),
0,
0,
size,
thrust::raw_pointer_cast(&d_data[0]),
thrust::raw_pointer_cast(&d_output[0]));
HIP_CHECK(hipGetLastError());
ASSERT_EQ(h_output, d_output);
}
}
}
template <typename T>
struct const_ref_plus_mod3
{
T* table;
const_ref_plus_mod3(T* table)
: table(table)
{}
THRUST_HOST_DEVICE const T& operator()(T a, T b)
{
return table[(int) (a + b)];
}
};
TYPED_TEST(ScanTests, TestInclusiveScanWithConstAccumulator)
{
// add numbers modulo 3 with external lookup table
using Vector = typename TestFixture::input_type;
using T = typename Vector::value_type;
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
Vector data{0, 1, 2, 1, 2, 0, 1};
Vector table{0, 1, 2, 0, 1, 2};
thrust::inclusive_scan(
data.begin(), data.end(), data.begin(), const_ref_plus_mod3<T>(thrust::raw_pointer_cast(&table[0])));
ASSERT_EQ(data, (Vector{0, 1, 0, 1, 0, 0, 1}));
}
struct only_set_when_expected_it
{
long long expected;
bool* flag;
THRUST_HOST_DEVICE only_set_when_expected_it operator++() const
{
return *this;
}
THRUST_HOST_DEVICE only_set_when_expected_it operator*() const
{
return *this;
}
template <typename Difference>
THRUST_HOST_DEVICE only_set_when_expected_it operator+(Difference) const
{
return *this;
}
template <typename Index>
THRUST_HOST_DEVICE only_set_when_expected_it operator[](Index) const
{
return *this;
}
THRUST_DEVICE void operator=(long long value) const
{
if (value == expected)
{
*flag = true;
}
}
};
THRUST_NAMESPACE_BEGIN
template <>
struct iterator_traits<only_set_when_expected_it>
{
using value_type = long long;
using reference = only_set_when_expected_it;
};
THRUST_NAMESPACE_END
namespace std
{
template <>
struct iterator_traits<only_set_when_expected_it>
{
using value_type = long long;
using reference = only_set_when_expected_it;
};
} // namespace std
void TestInclusiveScanWithBigIndexesHelper(int magnitude)
{
thrust::constant_iterator<long long> begin(1);
thrust::constant_iterator<long long> end = begin + (1ll << magnitude);
ASSERT_EQ(thrust::distance(begin, end), 1ll << magnitude);
thrust::device_ptr<bool> has_executed = thrust::device_malloc<bool>(1);
*has_executed = false;
only_set_when_expected_it out = {(1ll << magnitude), thrust::raw_pointer_cast(has_executed)};
thrust::inclusive_scan(thrust::device, begin, end, out);
bool has_executed_h = *has_executed;
thrust::device_free(has_executed);
ASSERT_EQ(has_executed_h, true);
}
TEST(ScanTests, TestInclusiveScanWithBigIndexes)
{
#ifdef ADDRESS_SANITIZER_BUILD
GTEST_SKIP() << "Skipping test due to memory constraints in address sanitizer build.";
#endif
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
TestInclusiveScanWithBigIndexesHelper(30);
TestInclusiveScanWithBigIndexesHelper(31);
#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE
TestInclusiveScanWithBigIndexesHelper(32);
TestInclusiveScanWithBigIndexesHelper(33);
#endif
}
void TestExclusiveScanWithBigIndexesHelper(int magnitude)
{
thrust::constant_iterator<long long> begin(1);
thrust::constant_iterator<long long> end = begin + (1ll << magnitude);
ASSERT_EQ(thrust::distance(begin, end), 1ll << magnitude);
thrust::device_ptr<bool> has_executed = thrust::device_malloc<bool>(1);
*has_executed = false;
only_set_when_expected_it out = {(1ll << magnitude) - 1, thrust::raw_pointer_cast(has_executed)};
thrust::exclusive_scan(thrust::device, begin, end, out, 0ll);
bool has_executed_h = *has_executed;
thrust::device_free(has_executed);
ASSERT_EQ(has_executed_h, true);
}
TEST(ScanTests, TestExclusiveScanWithBigIndexes)
{
#ifdef ADDRESS_SANITIZER_BUILD
GTEST_SKIP() << "Skipping test due to memory constraints in address sanitizer build.";
#endif
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
TestExclusiveScanWithBigIndexesHelper(30);
TestExclusiveScanWithBigIndexesHelper(31);
#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE
TestExclusiveScanWithBigIndexesHelper(32);
TestExclusiveScanWithBigIndexesHelper(33);
#endif
}
struct Int
{
int i{};
THRUST_HOST_DEVICE explicit Int(int num)
: i(num)
{}
THRUST_HOST_DEVICE Int()
: i{}
{}
THRUST_HOST_DEVICE Int operator+(Int const& o) const
{
return Int{this->i + o.i};
}
};
TEST(ScanTests, TestInclusiveScanWithUserDefinedType)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
thrust::device_vector<Int> vec(5, Int{1});
thrust::inclusive_scan(thrust::device, vec.cbegin(), vec.cend(), vec.begin());
ASSERT_EQ(static_cast<Int>(vec.back()).i, 5);
}
// Represents a permutation as a tuple of integers, see also: https://en.wikipedia.org/wiki/Permutation
// We need a distinct type (instead of an alias) for operator<< to be found via ADL
struct permutation_t : _THRUST_STD::array<int, 5>
{
permutation_t() = default;
constexpr THRUST_HOST_DEVICE permutation_t(int a, int b, int c, int d, int e)
: _THRUST_STD::array<int, 5>{a, b, c, d, e}
{}
friend std::ostream& operator<<(std::ostream& os, const permutation_t& p)
{
os << '{';
for (std::size_t i = 0; i < p.size(); i++)
{
if (i > 0)
{
os << ", ";
}
os << p[i];
}
return os << '}';
}
friend THRUST_HOST_DEVICE bool operator==(const permutation_t& lhs, const permutation_t& rhs)
{
auto lhs_ptr = lhs.data();
auto rhs_ptr = rhs.data();
for (size_t i = 0; i < lhs.size(); ++i)
{
if (lhs_ptr[i] != rhs_ptr[i])
{
return false;
}
}
return true;
}
};
// Composes two permutations. This operation is associative, but not commutative.
struct composition_op_t
{
THRUST_HOST_DEVICE permutation_t operator()(permutation_t lhs, permutation_t rhs) const
{
permutation_t result;
// Get raw pointers to the underlying data to avoid operator[] which
// results in debug-assert calls (and __glibcxx_assert_fail) on device.
auto lhs_ptr = lhs.data();
auto rhs_ptr = rhs.data();
auto result_ptr = result.data();
for (std::size_t i = 0; i < lhs.size(); i++)
{
result_ptr[i] = rhs_ptr[lhs_ptr[i]];
}
return result;
}
};
TEST(ScanTests, TestInclusiveScanWithNonCommutativeOp)
{
SCOPED_TRACE(testing::Message() << "with device_id= " << test::set_device_from_ctest());
const thrust::device_vector<permutation_t> input = {
{3, 2, 0, 1, 4},
{2, 4, 0, 1, 3},
{3, 2, 1, 4, 0},
{4, 3, 1, 0, 2},
{0, 3, 2, 4, 1},
{3, 2, 1, 0, 4},
{3, 4, 1, 2, 0},
{4, 2, 1, 0, 3},
{4, 0, 1, 3, 2},
{0, 2, 3, 1, 4}};
thrust::device_vector<permutation_t> output(10);
constexpr auto identity = permutation_t{0, 1, 2, 3, 4};
thrust::inclusive_scan(input.begin(), input.end(), output.begin(), composition_op_t{});
ASSERT_EQ(
output,
(thrust::device_vector<permutation_t>{
{3, 2, 0, 1, 4},
{1, 0, 2, 4, 3},
{2, 3, 1, 0, 4},
{1, 0, 3, 4, 2},
{3, 0, 4, 1, 2},
{0, 3, 4, 2, 1},
{3, 2, 0, 1, 4},
{0, 1, 4, 2, 3},
{4, 0, 2, 1, 3},
{4, 0, 3, 2, 1}}));
thrust::exclusive_scan(input.begin(), input.end(), output.begin(), identity, composition_op_t{});
ASSERT_EQ(
output,
(thrust::device_vector<permutation_t>{
{0, 1, 2, 3, 4},
{3, 2, 0, 1, 4},
{1, 0, 2, 4, 3},
{2, 3, 1, 0, 4},
{1, 0, 3, 4, 2},
{3, 0, 4, 1, 2},
{0, 3, 4, 2, 1},
{3, 2, 0, 1, 4},
{0, 1, 4, 2, 3},
{4, 0, 2, 1, 3}}));
}