// Copyright (c) 2024-2025 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. /* * 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. */ /*! \file thrust/system/hip/hipstdpar/impl/numeric.hpp * \brief Numeric operations implementation detail header for HIPSTDPAR. */ #pragma once #if defined(__HIPSTDPAR__) # include # include # include # include # include # include # include # include # include # include # include # include # include "hipstd.hpp" namespace std { // BEGIN ADJACENT_DIFFERENCE template ()>* = nullptr> inline O adjacent_difference(execution::parallel_unsequenced_policy, I fi, I li, O fo) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::adjacent_difference(::thrust::device, fi, li, fo); } template ()>* = nullptr> inline O adjacent_difference(execution::parallel_unsequenced_policy, I fi, I li, O fo) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); return ::std::adjacent_difference(::std::execution::par, fi, li, fo); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline O adjacent_difference(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::adjacent_difference(::thrust::device, fi, li, fo, ::std::move(op)); } template () || !::hipstd::is_offloadable_callable()>* = nullptr> inline O adjacent_difference(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::adjacent_difference(::std::execution::par, fi, li, fo, ::std::move(op)); } // END ADJACENT_DIFFERENCE // BEGIN REDUCE template ()>* = nullptr> inline typename iterator_traits::value_type reduce(execution::parallel_unsequenced_policy, I f, I l) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::reduce(::thrust::device, f, l); } template ()>* = nullptr> inline typename iterator_traits::value_type reduce(execution::parallel_unsequenced_policy, I f, I l) { ::hipstd::unsupported_iterator_category::iterator_category>(); return ::std::reduce(::std::execution::par, f, l); } template ()>* = nullptr> inline T reduce(execution::parallel_unsequenced_policy, I f, I l, T x) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::reduce(::thrust::device, f, l, ::std::move(x)); } template ()>* = nullptr> inline T reduce(execution::parallel_unsequenced_policy, I f, I l, T x) { ::hipstd::unsupported_iterator_category::iterator_category>(); return ::std::reduce(::std::execution::par, f, l, ::std::move(x)); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline T reduce(execution::parallel_unsequenced_policy, I f, I l, T x, Op op) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::reduce(::thrust::device, f, l, ::std::move(x), ::std::move(op)); } template () || !::hipstd::is_offloadable_callable()>* = nullptr> inline T reduce(execution::parallel_unsequenced_policy, I f, I l, T x, Op op) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::reduce(::std::execution::par, f, l, ::std::move(x), ::std::move(op)); } // END REDUCE // BEGIN EXCLUSIVE_SCAN template ()>* = nullptr> inline O exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::exclusive_scan(::thrust::device, fi, li, fo, ::std::move(x)); } template ()>* = nullptr> inline O exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x) { ::hipstd::unsupported_iterator_category::iterator_category, typename std::iterator_traits::iterator_category>(); return ::std::exclusive_scan(::std::execution::par, fi, li, fo, ::std::move(x)); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline O exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x, Op op) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::exclusive_scan(::thrust::device, fi, li, fo, ::std::move(x), ::std::move(op)); } template () || !::hipstd::is_offloadable_callable()>* = nullptr> inline O exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x, Op op) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::exclusive_scan(::std::execution::par, fi, li, fo, ::std::move(x), ::std::move(op)); } // END EXCLUSIVE_SCAN // BEGIN INCLUSIVE_SCAN template ()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::inclusive_scan(::thrust::device, fi, li, fo); } template ()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); return ::std::inclusive_scan(::std::execution::par, fi, li, fo); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::inclusive_scan(::thrust::device, fi, li, fo, ::std::move(op)); } template () || !::hipstd::is_offloadable_callable()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::inclusive_scan(::std::execution::par, fi, li, fo, ::std::move(op)); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op, T x) { // TODO: this is highly inefficient due to rocThrust not exposing // this particular interface where the user provides x. ::hipstd::warn_if_no_xnack(); if (fi == li) { return fo; } ::hipstd::__maybe_bind_globals(); auto lo = ::thrust::inclusive_scan(::thrust::device, fi, li, fo, op); auto fn = [op = ::std::move(op), x = ::std::move(x)](auto&& y) { return op(x, y); }; using fn_t = decltype(fn); if constexpr (::std::is_trivially_destructible_v) { return ::thrust::transform(::thrust::device, fo, lo, fo, ::std::move(fn)); } else { ::hipstd::detail::device_callable_guard guard(::std::move(fn)); O result; try { result = ::thrust::transform( ::thrust::device, fo, lo, fo, ::hipstd::detail::callable_proxy{guard.get()}); } catch (...) { (void) ::hipDeviceSynchronize(); throw; } ::thrust::hip_rocprim::throw_on_error( ::hipDeviceSynchronize(), "hipstdpar inclusive_scan: failed to synchronize"); guard.destroy_and_free(); return result; } } template () || !::hipstd::is_offloadable_callable()>* = nullptr> inline O inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op op, T x) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::inclusive_scan(::std::execution::par, fi, li, fo, ::std::move(op), ::std::move(x)); } // END INCLUSIVE_SCAN // BEGIN TRANSFORM_REDUCE template ()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I0 f0, I0 l0, I1 f1, T x) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::inner_product(::thrust::device, f0, l0, f1, ::std::move(x)); } template ()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I0 f0, I0 l0, I1 f1, T x) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); return ::std::transform_reduce(::std::execution::par, f0, l0, f1, ::std::move(x)); } template < typename I0, typename I1, typename T, typename Op0, typename Op1, enable_if_t<::hipstd::is_offloadable_iterator() && ::hipstd::is_offloadable_callable()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I0 f0, I0 l0, I1 f1, T x, Op0 op0, Op1 op1) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::inner_product(::thrust::device, f0, l0, f1, ::std::move(x), ::std::move(op0), ::std::move(op1)); } template < typename I0, typename I1, typename T, typename Op0, typename Op1, enable_if_t() || !::hipstd::is_offloadable_callable()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I0 f0, I0 l0, I1 f1, T x, Op0 op0, Op1 op1) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::transform_reduce(::std::execution::par, f0, l0, f1, ::std::move(x), ::std::move(op0), ::std::move(op1)); } template () && ::hipstd::is_offloadable_callable()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I f, I l, T x, Op0 op0, Op1 op1) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::transform_reduce(::thrust::device, f, l, ::std::move(op1), ::std::move(x), ::std::move(op0)); } template < typename I, typename T, typename Op0, typename Op1, enable_if_t() || !::hipstd::is_offloadable_callable()>* = nullptr> inline T transform_reduce(execution::parallel_unsequenced_policy, I f, I l, T x, Op0 op0, Op1 op1) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::transform_reduce(::std::execution::par, f, l, ::std::move(x), ::std::move(op0), ::std::move(op1)); } // END TRANSFORM_REDUCE // BEGIN TRANSFORM_EXCLUSIVE_SCAN template < typename I, typename O, typename T, typename Op0, typename Op1, enable_if_t<::hipstd::is_offloadable_iterator() && ::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x, Op0 op0, Op1 op1) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::transform_exclusive_scan( ::thrust::device, fi, li, fo, ::std::move(op1), ::std::move(x), ::std::move(op0)); } template < typename I, typename O, typename T, typename Op0, typename Op1, enable_if_t() || !::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_exclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, T x, Op0 op0, Op1 op1) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::transform_exclusive_scan( ::std::execution::par, fi, li, fo, ::std::move(x), ::std::move(op0), ::std::move(op1)); } // END TRANSFORM_EXCLUSIVE_SCAN // BEGIN TRANSFORM_INCLUSIVE_SCAN template < typename I, typename O, typename Op0, typename Op1, enable_if_t<::hipstd::is_offloadable_iterator() && ::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op0 op0, Op1 op1) { ::hipstd::__maybe_bind_globals(); ::hipstd::warn_if_no_xnack(); return ::thrust::transform_inclusive_scan(::thrust::device, fi, li, fo, ::std::move(op1), ::std::move(op0)); } template < typename I, typename O, typename Op0, typename Op1, enable_if_t() || !::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op0 op0, Op1 op1) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::transform_inclusive_scan(::std::execution::par, fi, li, fo, ::std::move(op0), ::std::move(op1)); } template < typename I, typename O, typename Op0, typename Op1, typename T, enable_if_t<::hipstd::is_offloadable_iterator() && ::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op0 op0, Op1 op1, T x) { // TODO: this is inefficient. ::hipstd::warn_if_no_xnack(); if (fi == li) { return fo; } ::hipstd::__maybe_bind_globals(); auto lo = ::thrust::transform_inclusive_scan(::thrust::device, fi, li, fo, ::std::move(op1), op0); auto fn = [op0 = ::std::move(op0), x = ::std::move(x)](auto&& y) { return op0(x, y); }; using fn_t = decltype(fn); if constexpr (::std::is_trivially_destructible_v) { return ::thrust::transform(::thrust::device, fo, lo, fo, ::std::move(fn)); } else { ::hipstd::detail::device_callable_guard guard(::std::move(fn)); O result; try { result = ::thrust::transform( ::thrust::device, fo, lo, fo, ::hipstd::detail::callable_proxy{guard.get()}); } catch (...) { (void) ::hipDeviceSynchronize(); throw; } ::thrust::hip_rocprim::throw_on_error( ::hipDeviceSynchronize(), "hipstdpar transform_inclusive_scan: failed to synchronize"); guard.destroy_and_free(); return result; } } template < typename I, typename O, typename Op0, typename Op1, typename T, enable_if_t() || !::hipstd::is_offloadable_callable()>* = nullptr> inline O transform_inclusive_scan(execution::parallel_unsequenced_policy, I fi, I li, O fo, Op0 op0, Op1 op1, T x) { if constexpr (!::hipstd::is_offloadable_iterator()) { ::hipstd::unsupported_iterator_category::iterator_category, typename iterator_traits::iterator_category>(); } if constexpr (!::hipstd::is_offloadable_callable()) { ::hipstd::unsupported_callable_type(); } return ::std::transform_inclusive_scan( ::std::execution::par, fi, li, fo, ::std::move(op0), ::std::move(op1), ::std::move(x)); } // END TRANSFORM_INCLUSIVE_SCAN } // namespace std #else // __HIPSTDPAR__ # error "__HIPSTDPAR__ should be defined. Please use the '--hipstdpar' compile option." #endif // __HIPSTDPAR__