#include #include #include #include #include template struct div_n_equality_op { T div; __host__ __device__ bool operator()(const T x, const T& y) const { return (x / div) == (y / div); } }; template struct multiply_n { T multiplier; __host__ __device__ T operator()(T x) { return x * multiplier; } }; struct check_valid_item_op { ::cuda::std::uint32_t* error_counter{}; int expected_upper_bound{}; __device__ bool operator()(const int lhs, const int rhs) const { if (lhs > expected_upper_bound || rhs > expected_upper_bound) { if (error_counter) { atomicAdd(error_counter, 1); } return false; } return lhs == rhs; } }; #ifdef THRUST_TEST_DEVICE_SIDE template __global__ void unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result) { *result = thrust::unique(exec, first, last); } template __global__ void unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result) { *result = thrust::unique(exec, first, last, pred); } template void TestUniqueDevice(ExecutionPolicy exec) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; thrust::device_vector new_last_vec(1); Vector::iterator new_last; unique_kernel<<<1, 1>>>(exec, data.begin(), data.end(), new_last_vec.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } new_last = new_last_vec[0]; ASSERT_EQUAL(new_last - data.begin(), 7); data.erase(new_last, data.end()); Vector ref{11, 12, 20, 29, 21, 31, 37}; // should we consider calculating ref from std::algorithm if exists? ASSERT_EQUAL(data, ref); unique_kernel<<<1, 1>>>(exec, data.begin(), new_last, div_n_equality_op{10}, new_last_vec.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } new_last = new_last_vec[0]; ASSERT_EQUAL(new_last - data.begin(), 3); data.erase(new_last, data.end()); ref = {11, 20, 31}; ASSERT_EQUAL(data, ref); } void TestUniqueDeviceSeq() { TestUniqueDevice(thrust::seq); } DECLARE_UNITTEST(TestUniqueDeviceSeq); void TestUniqueDeviceDevice() { TestUniqueDevice(thrust::device); } DECLARE_UNITTEST(TestUniqueDeviceDevice); void TestUniqueDeviceNoSync() { TestUniqueDevice(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueDeviceNoSync); #endif template void TestUniqueCudaStreams(ExecutionPolicy policy) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; thrust::device_vector new_last_vec(1); Vector::iterator new_last; cudaStream_t s; cudaStreamCreate(&s); auto streampolicy = policy.on(s); new_last = thrust::unique(streampolicy, data.begin(), data.end()); cudaStreamSynchronize(s); ASSERT_EQUAL(new_last - data.begin(), 7); data.erase(new_last, data.end()); Vector ref{11, 12, 20, 29, 21, 31, 37}; ASSERT_EQUAL(data, ref); new_last = thrust::unique(streampolicy, data.begin(), new_last, div_n_equality_op{10}); cudaStreamSynchronize(s); ASSERT_EQUAL(new_last - data.begin(), 3); data.erase(new_last, data.end()); ref = {11, 20, 31}; ASSERT_EQUAL(data, ref); cudaStreamDestroy(s); } void TestUniqueCudaStreamsSync() { TestUniqueCudaStreams(thrust::cuda::par); } DECLARE_UNITTEST(TestUniqueCudaStreamsSync); void TestUniqueCudaStreamsNoSync() { TestUniqueCudaStreams(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueCudaStreamsNoSync); #ifdef THRUST_TEST_DEVICE_SIDE template __global__ void unique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Iterator3 result2) { *result2 = thrust::unique_copy(exec, first, last, result1); } template __global__ void unique_copy_kernel( ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, BinaryPredicate pred, Iterator3 result2) { *result2 = thrust::unique_copy(exec, first, last, result1, pred); } template void TestUniqueCopyDevice(ExecutionPolicy exec) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; Vector output(10, -1); thrust::device_vector new_last_vec(1); Vector::iterator new_last; unique_copy_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin(), new_last_vec.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } new_last = new_last_vec[0]; ASSERT_EQUAL(new_last - output.begin(), 7); output.erase(new_last, output.end()); Vector ref{11, 12, 20, 29, 21, 31, 37}; ASSERT_EQUAL(output, ref); unique_copy_kernel<<<1, 1>>>( exec, output.begin(), new_last, data.begin(), div_n_equality_op{10}, new_last_vec.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } new_last = new_last_vec[0]; ASSERT_EQUAL(new_last - data.begin(), 3); data.erase(new_last, data.end()); ref = {11, 20, 31}; ASSERT_EQUAL(data, ref); } void TestUniqueCopyDeviceSeq() { TestUniqueCopyDevice(thrust::seq); } DECLARE_UNITTEST(TestUniqueCopyDeviceSeq); void TestUniqueCopyDeviceDevice() { TestUniqueCopyDevice(thrust::device); } DECLARE_UNITTEST(TestUniqueCopyDeviceDevice); void TestUniqueCopyDeviceNoSync() { TestUniqueCopyDevice(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueCopyDeviceNoSync); #endif template void TestUniqueCopyCudaStreams(ExecutionPolicy policy) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; Vector output(10, -1); thrust::device_vector new_last_vec(1); Vector::iterator new_last; cudaStream_t s; cudaStreamCreate(&s); auto streampolicy = policy.on(s); new_last = thrust::unique_copy(streampolicy, data.begin(), data.end(), output.begin()); cudaStreamSynchronize(s); ASSERT_EQUAL(new_last - output.begin(), 7); output.erase(new_last, output.end()); Vector ref{11, 12, 20, 29, 21, 31, 37}; ASSERT_EQUAL(output, ref); new_last = thrust::unique_copy(streampolicy, output.begin(), new_last, data.begin(), div_n_equality_op{10}); cudaStreamSynchronize(s); ASSERT_EQUAL(new_last - data.begin(), 3); data.erase(new_last, data.end()); ref = {11, 20, 31}; ASSERT_EQUAL(data, ref); cudaStreamDestroy(s); } void TestUniqueCopyCudaStreamsSync() { TestUniqueCopyCudaStreams(thrust::cuda::par); } DECLARE_UNITTEST(TestUniqueCopyCudaStreamsSync); void TestUniqueCopyCudaStreamsNoSync() { TestUniqueCopyCudaStreams(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueCopyCudaStreamsNoSync); #ifdef THRUST_TEST_DEVICE_SIDE template __global__ void unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result) { *result = thrust::unique_count(exec, first, last); } template __global__ void unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result) { *result = thrust::unique_count(exec, first, last, pred); } template void TestUniqueCountDevice(ExecutionPolicy exec) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; Vector output(1, -1); unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } ASSERT_EQUAL(output[0], 7); unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), div_n_equality_op{10}, output.begin()); { cudaError_t const err = cudaDeviceSynchronize(); ASSERT_EQUAL(cudaSuccess, err); } ASSERT_EQUAL(output[0], 3); } void TestUniqueCountDeviceSeq() { TestUniqueCountDevice(thrust::seq); } DECLARE_UNITTEST(TestUniqueCountDeviceSeq); void TestUniqueCountDeviceDevice() { TestUniqueCountDevice(thrust::device); } DECLARE_UNITTEST(TestUniqueCountDeviceDevice); void TestUniqueCountDeviceNoSync() { TestUniqueCountDevice(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueCountDeviceNoSync); #endif template void TestUniqueCountCudaStreams(ExecutionPolicy policy) { using Vector = thrust::device_vector; using T = Vector::value_type; Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37}; cudaStream_t s; cudaStreamCreate(&s); auto streampolicy = policy.on(s); int result = thrust::unique_count(streampolicy, data.begin(), data.end()); cudaStreamSynchronize(s); ASSERT_EQUAL(result, 7); result = thrust::unique_count(streampolicy, data.begin(), data.end(), div_n_equality_op{10}); cudaStreamSynchronize(s); ASSERT_EQUAL(result, 3); cudaStreamDestroy(s); } void TestUniqueCountCudaStreamsSync() { TestUniqueCountCudaStreams(thrust::cuda::par); } DECLARE_UNITTEST(TestUniqueCountCudaStreamsSync); void TestUniqueCountCudaStreamsNoSync() { TestUniqueCountCudaStreams(thrust::cuda::par_nosync); } DECLARE_UNITTEST(TestUniqueCountCudaStreamsNoSync); void TestUniqueWithMagnitude(int magnitude) { using offset_t = std::int64_t; using equality_op_t = div_n_equality_op; offset_t run_length_of_equal_items = offset_t{10}; equality_op_t equality_op = equality_op_t{run_length_of_equal_items}; // Prepare input offset_t num_items = offset_t{1ull} << magnitude; thrust::counting_iterator begin(offset_t{0}); auto end = begin + num_items; ASSERT_EQUAL(static_cast(cuda::std::distance(begin, end)), num_items); offset_t expected_num_unique = ::cuda::ceil_div(num_items, offset_t{10}); thrust::device_vector unique_out(expected_num_unique); auto unique_out_end = thrust::unique_copy(begin, end, unique_out.begin(), equality_op); // Ensure number of selected items are correct offset_t num_selected_out = static_cast(cuda::std::distance(unique_out.begin(), unique_out_end)); ASSERT_EQUAL(num_selected_out, expected_num_unique); unique_out.resize(expected_num_unique); // Ensure selected items are correct auto expected_out_it = thrust::make_transform_iterator(begin, multiply_n{run_length_of_equal_items}); bool all_results_correct = thrust::equal(unique_out.begin(), unique_out.end(), expected_out_it); ASSERT_EQUAL(all_results_correct, true); } void TestUniqueWithLargeNumberOfItems() try { for (int mag : {30, 31, 32, 33}) { TestUniqueWithMagnitude(mag); } } catch (std::bad_alloc&) { // if we run out of memory, just skip the test return; } DECLARE_UNITTEST(TestUniqueWithLargeNumberOfItems); void TestUniqueWithCustomEqualityOp() { using Vector = thrust::device_vector; using T = Vector::value_type; auto constexpr num_items = 1000; auto data = thrust::make_counting_iterator(T{0}); thrust::device_vector<::cuda::std::uint32_t> error_counter(1, 0); auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data()); Vector unique_out(num_items); auto unique_out_end = thrust::unique_copy( data, data + num_items, unique_out.begin(), check_valid_item_op{error_counter_ptr, num_items - 1}); auto num_selected_out = cuda::std::distance(unique_out.begin(), unique_out_end); ASSERT_EQUAL(num_selected_out, num_items); ASSERT_EQUAL(error_counter[0], ::cuda::std::uint32_t{0}); bool all_results_correct = thrust::equal(unique_out.cbegin(), unique_out.cend(), data); ASSERT_EQUAL(all_results_correct, true); } DECLARE_UNITTEST(TestUniqueWithCustomEqualityOp); template struct NonConstAdapter { F f; NonConstAdapter(const F& func) : f(func) {} template __device__ auto operator()(Args&&... args) -> decltype(f(cuda::std::forward(args)...)) { return f(cuda::std::forward(args)...); } }; void TestUniqueWithCustomEqualityOpMutable() { using Vector = thrust::device_vector; thrust::device_vector in = {1, 1, 2, 3, 4, 4, 5}; thrust::unique(thrust::cuda::par, in.begin(), in.end(), NonConstAdapter(cuda::std::equal_to<>{})); } DECLARE_UNITTEST(TestUniqueWithCustomEqualityOpMutable);