/****************************************************************************** * 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 # if !_THRUST_HAS_DEVICE_SYSTEM_STD # include # endif THRUST_NAMESPACE_BEGIN namespace hip_rocprim { // XXX forward declare to circumvent circular dependency template InputIt THRUST_HOST_DEVICE find_if(execution_policy& policy, InputIt first, InputIt last, Predicate predicate); template InputIt THRUST_HOST_DEVICE find_if_not(execution_policy& policy, InputIt first, InputIt last, Predicate predicate); template InputIt THRUST_HOST_DEVICE find(execution_policy& policy, InputIt first, InputIt last, T const& value); }; // namespace hip_rocprim THRUST_NAMESPACE_END # include # include THRUST_NAMESPACE_BEGIN namespace hip_rocprim { namespace __find_if { template struct functor { THRUST_HIP_DEVICE_FUNCTION TupleType operator()(const TupleType& lhs, const TupleType& rhs) const { // select the smallest index among true results if (thrust::get<0>(lhs) && thrust::get<0>(rhs)) { return TupleType(true, (thrust::min)(thrust::get<1>(lhs), thrust::get<1>(rhs))); } else if (thrust::get<0>(lhs)) { return lhs; } else { return rhs; } } }; template struct transform_input_iterator_t { using self_t = transform_input_iterator_t; using difference_type = typename iterator_traits::difference_type; using value_type = ValueType; using pointer = void; using reference = value_type; using iterator_category = _THRUST_STD::random_access_iterator_tag; InputIt input; mutable UnaryOp op; THRUST_HOST_DEVICE THRUST_FORCEINLINE transform_input_iterator_t(InputIt input, UnaryOp op) : input(input) , op(op) {} transform_input_iterator_t(const self_t&) = default; // UnaryOp might not be copy assignable, such as when it is a lambda. Define // an explicit copy assignment operator that doesn't try to assign it. THRUST_HOST_DEVICE self_t& operator=(const self_t& o) { input = o.input; return *this; } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t operator++(int) { self_t retval = *this; ++input; return retval; } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t operator++() { ++input; return *this; } THRUST_HOST_DEVICE THRUST_FORCEINLINE reference operator*() const { typename thrust::iterator_value::type x = *input; return op(x); } THRUST_HOST_DEVICE THRUST_FORCEINLINE reference operator*() { typename thrust::iterator_value::type x = *input; return op(x); } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t operator+(difference_type n) const { return self_t(input + n, op); } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t& operator+=(difference_type n) { input += n; return *this; } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t operator-(difference_type n) const { return self_t(input - n, op); } THRUST_HOST_DEVICE THRUST_FORCEINLINE self_t& operator-=(difference_type n) { input -= n; return *this; } THRUST_HOST_DEVICE THRUST_FORCEINLINE difference_type operator-(self_t other) const { return input - other.input; } THRUST_HOST_DEVICE THRUST_FORCEINLINE reference operator[](difference_type n) const { return op(input[n]); } THRUST_HOST_DEVICE THRUST_FORCEINLINE bool operator==(const self_t& rhs) const { return (input == rhs.input); } THRUST_HOST_DEVICE THRUST_FORCEINLINE bool operator!=(const self_t& rhs) const { return (input != rhs.input); } }; } // namespace __find_if template InputIt THRUST_HOST_DEVICE find_if_n(execution_policy& policy, InputIt first, Size num_items, Predicate predicate) { using result_type = typename thrust::tuple; // empty sequence if (num_items == 0) { return first; } // this implementation breaks up the sequence into separate intervals // in an attempt to early-out as soon as a value is found // // XXX compose find_if from a look-back prefix scan algorithm // and abort kernel when the first element is found // TODO incorporate sizeof(InputType) into interval_threshold and round to multiple of 32 const Size interval_threshold = 1 << 20; const Size interval_size = (thrust::min)(interval_threshold, num_items); // FIXME(bgruber): we should also be able to use transform_iterator here, but it makes nvc++ hang. See: // https://github.com/NVIDIA/cccl/issues/3594. The problem does not occur with nvcc, so we could not add a test :/ using XfrmIterator = __find_if::transform_input_iterator_t; // using XfrmIterator = transform_iterator; using IteratorTuple = thrust::tuple>; using ZipIterator = thrust::zip_iterator; IteratorTuple iter_tuple = thrust::make_tuple(XfrmIterator(first, predicate), counting_iterator(0)); ZipIterator begin = thrust::make_zip_iterator(iter_tuple); ZipIterator end = begin + num_items; for (ZipIterator interval_begin = begin; interval_begin < end; interval_begin += interval_size) { ZipIterator interval_end = interval_begin + interval_size; if (end < interval_end) { interval_end = end; } // end if result_type result = reduce( policy, interval_begin, interval_end, result_type(false, interval_end - begin), __find_if::functor()); // see if we found something if (thrust::get<0>(result)) { return first + thrust::get<1>(result); } } // nothing was found if we reach here... return first + num_items; } template InputIt THRUST_HOST_DEVICE find_if(execution_policy& policy, InputIt first, InputIt last, Predicate predicate) { return hip_rocprim::find_if_n(policy, first, thrust::distance(first, last), predicate); } template InputIt THRUST_HOST_DEVICE find_if_not(execution_policy& policy, InputIt first, InputIt last, Predicate predicate) { return hip_rocprim::find_if(policy, first, last, thrust::not_fn(predicate)); } template InputIt THRUST_HOST_DEVICE find(execution_policy& policy, InputIt first, InputIt last, T const& value) { using thrust::placeholders::_1; return hip_rocprim::find_if(policy, first, last, _1 == value); } } // namespace hip_rocprim THRUST_NAMESPACE_END #endif