/****************************************************************************** * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Modifications Copyright (c) 2019-2025, Advanced Micro Devices, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the NVIDIA CORPORATION nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ #pragma once #include #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) # pragma GCC system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) # pragma clang system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) # pragma system_header #endif // no system header #if THRUST_DEVICE_COMPILER == THRUST_DEVICE_COMPILER_HIP # include # include # include # include # include # include # include # include # include # include # include # include // rocPRIM includes # include # include THRUST_NAMESPACE_BEGIN // XXX declare generic copy_if interface // to avoid circular dependency from thrust/copy.h template THRUST_HOST_DEVICE OutputIterator copy_if( const thrust::detail::execution_policy_base& exec, InputIterator first, InputIterator last, OutputIterator result, Predicate pred); template THRUST_HOST_DEVICE OutputIterator copy_if( const thrust::detail::execution_policy_base& exec, InputIterator1 first, InputIterator1 last, InputIterator2 stencil, OutputIterator result, Predicate pred); namespace hip_rocprim { namespace __copy_if { template ROCPRIM_KERNEL void copy_if_kernel(InputIt first, BoolIt flagsFirst, IntIt posFirst, const size_t size, OutputIt output) { const size_t baseIdx = (blockIdx.x * blockDim.x + threadIdx.x) * ItemsPerThread; for (size_t i = 0; i < ItemsPerThread; ++i) { const size_t index = baseIdx + i; if (index < size) { if (flagsFirst[index]) { output[posFirst[index] - 1] = first[index]; } } } } template THRUST_RUNTIME_FUNCTION auto copy_if(execution_policy& policy, InputIt first, InputIt last, OutputIt output, Predicate predicate) -> std::enable_if_t::value_type) < 512, OutputIt> { using namespace thrust::system::hip_rocprim::temp_storage; using size_type = typename iterator_traits::difference_type; size_type num_items = thrust::distance(first, last); size_t temp_storage_bytes = 0; hipStream_t stream = hip_rocprim::stream(policy); bool debug_sync = THRUST_HIP_DEBUG_SYNC_FLAG; if (num_items == 0) { return output; } // Determine temporary device storage requirements. hip_rocprim::throw_on_error( rocprim::select( nullptr, temp_storage_bytes, first, output, static_cast(nullptr), num_items, predicate, stream, debug_sync), "copy_if failed on 1st step"); size_t storage_size; void* ptr = nullptr; void* temp_stor = nullptr; size_type* d_num_selected_out; auto l_part = make_linear_partition(make_partition(&temp_stor, temp_storage_bytes), ptr_aligned_array(&d_num_selected_out, 1)); // Calculate storage_size including alignment hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part)); // Allocate temporary storage. thrust::detail::temporary_array tmp(policy, storage_size); ptr = static_cast(tmp.data().get()); // Create pointers with alignment hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part)); hip_rocprim::throw_on_error( rocprim::select( ptr, temp_storage_bytes, first, output, d_num_selected_out, num_items, predicate, stream, debug_sync), "copy_if failed on 2nd step"); size_type num_selected = get_value(policy, d_num_selected_out); return output + num_selected; } template THRUST_RUNTIME_FUNCTION auto copy_if(execution_policy& policy, InputIt first, InputIt last, OutputIt output, Predicate predicate) -> std::enable_if_t::value_type) < 512), OutputIt> { using namespace thrust::system::hip_rocprim::temp_storage; using size_type = typename iterator_traits::difference_type; size_type num_items = thrust::distance(first, last); hipStream_t stream = hip_rocprim::stream(policy); bool debug_sync = THRUST_HIP_DEBUG_SYNC_FLAG; thrust::detail::temporary_array flags(policy, num_items); hip_rocprim::throw_on_error( rocprim::transform( first, flags.begin(), num_items, [predicate] __host__ __device__(auto const& val) { return predicate(val) ? 1 : 0; }, stream, debug_sync), "copy_if failed on transform"); thrust::detail::temporary_array pos(policy, num_items); thrust::inclusive_scan(policy, flags.begin(), flags.end(), pos.begin()); constexpr static size_t items_per_thread = 16; constexpr static size_t threads_per_block = 256; const size_t block_size = std::ceil(static_cast(num_items) / 16 / threads_per_block); copy_if_kernel <<>>(first, flags.begin(), pos.begin(), num_items, output); return output + get_value(policy, &pos[num_items - 1]); } template THRUST_RUNTIME_FUNCTION OutputIt copy_if( execution_policy& policy, InputIt first, InputIt last, StencilIt stencil, OutputIt output, Predicate predicate) { using namespace thrust::system::hip_rocprim::temp_storage; using size_type = typename iterator_traits::difference_type; size_type num_items = static_cast(thrust::distance(first, last)); size_t temp_storage_bytes = 0; hipStream_t stream = hip_rocprim::stream(policy); bool debug_sync = THRUST_HIP_DEBUG_SYNC_FLAG; if (num_items == 0) { return output; } auto flags = thrust::make_transform_iterator(stencil, predicate); // Query temporary storage requirements hip_rocprim::throw_on_error( rocprim::select( nullptr, temp_storage_bytes, first, flags, output, static_cast(nullptr), num_items, stream, debug_sync), "copy_if failed on 1st step"); size_t storage_size; void* ptr = nullptr; void* temp_stor = nullptr; size_type* d_num_selected_out; auto l_part = make_linear_partition(make_partition(&temp_stor, temp_storage_bytes), ptr_aligned_array(&d_num_selected_out, 1)); // Allocate temporary storage. hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part)); // Allocate temporary storage. thrust::detail::temporary_array tmp(policy, storage_size); ptr = static_cast(tmp.data().get()); // Create pointers with alignment hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part)); hip_rocprim::throw_on_error( rocprim::select(ptr, temp_storage_bytes, first, flags, output, d_num_selected_out, num_items, stream, debug_sync), "copy_if failed on 2nd step"); size_type num_selected = get_value(policy, d_num_selected_out); return output + num_selected; } } // namespace __copy_if //------------------------- // Thrust API entry points //------------------------- THRUST_EXEC_CHECK_DISABLE template OutputIterator THRUST_HOST_DEVICE copy_if( execution_policy& policy, InputIterator first, InputIterator last, OutputIterator result, Predicate pred) { // struct workaround is required for HIP-clang struct workaround { THRUST_HOST static OutputIterator par( execution_policy& policy, InputIterator first, InputIterator last, OutputIterator result, Predicate pred) { return __copy_if::copy_if(policy, first, last, result, pred); } # if !__THRUST_HAS_HIPRT__ THRUST_DEVICE static OutputIterator seq( execution_policy& policy, InputIterator first, InputIterator last, OutputIterator result, Predicate pred) { return thrust::copy_if(cvt_to_seq(derived_cast(policy)), first, last, result, pred); } # endif }; # if __THRUST_HAS_HIPRT__ return workaround::par(policy, first, last, result, pred); # else return workaround::seq(policy, first, last, result, pred); # endif } // func copy_if THRUST_EXEC_CHECK_DISABLE template OutputIterator THRUST_HOST_DEVICE copy_if( execution_policy& policy, InputIterator first, InputIterator last, StencilIterator stencil, OutputIterator result, Predicate pred) { // struct workaround is required for HIP-clang struct workaround { THRUST_HOST static OutputIterator par(execution_policy& policy, InputIterator first, InputIterator last, StencilIterator stencil, OutputIterator result, Predicate pred) { return __copy_if::copy_if(policy, first, last, stencil, result, pred); } # if !__THRUST_HAS_HIPRT__ THRUST_DEVICE static OutputIterator seq(execution_policy& policy, InputIterator first, InputIterator last, StencilIterator stencil, OutputIterator result, Predicate pred) { return thrust::copy_if(cvt_to_seq(derived_cast(policy)), first, last, stencil, result, pred); } # endif }; # if __THRUST_HAS_HIPRT__ return workaround::par(policy, first, last, stencil, result, pred); # else return workaround::seq(policy, first, last, stencil, result, pred); # endif } // func copy_if } // namespace hip_rocprim THRUST_NAMESPACE_END # include #endif