Distributed Ranges
Loading...
Searching...
No Matches
reduce.hpp
1// SPDX-FileCopyrightText: Intel Corporation
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#pragma once
6
7#include <oneapi/dpl/execution>
8#include <oneapi/dpl/numeric>
9
10#include <oneapi/dpl/async>
11
12#include <dr/concepts/concepts.hpp>
13#include <dr/detail/onedpl_direct_iterator.hpp>
14#include <dr/sp/algorithms/execution_policy.hpp>
15#include <dr/sp/init.hpp>
16#include <sycl/sycl.hpp>
17
18namespace {
19
20// Precondition: rng::distance(first, last) >= 2
21// Postcondition: return future to [first, last) reduced with fn
22template <typename T, typename ExecutionPolicy,
23 std::bidirectional_iterator Iter, typename Fn>
24auto reduce_no_init_async(ExecutionPolicy &&policy, Iter first, Iter last,
25 Fn &&fn) {
26 Iter new_last = last;
27 --new_last;
28
29 std::iter_value_t<Iter> init = *new_last;
30
31 dr::__detail::direct_iterator d_first(first);
32 dr::__detail::direct_iterator d_last(new_last);
33
34 return oneapi::dpl::experimental::reduce_async(
35 std::forward<ExecutionPolicy>(policy), d_first, d_last,
36 static_cast<T>(init), std::forward<Fn>(fn));
37}
38
39template <typename T, typename ExecutionPolicy,
40 std::bidirectional_iterator Iter, typename Fn>
41 requires(sycl::has_known_identity_v<Fn, T>)
42auto reduce_no_init_async(ExecutionPolicy &&policy, Iter first, Iter last,
43 Fn &&fn) {
44 dr::__detail::direct_iterator d_first(first);
46
47 return oneapi::dpl::experimental::reduce_async(
48 std::forward<ExecutionPolicy>(policy), d_first, d_last,
49 sycl::known_identity_v<Fn, T>, std::forward<Fn>(fn));
50}
51
52} // namespace
53
54namespace dr::sp {
55
56template <typename ExecutionPolicy, dr::distributed_range R, typename T,
57 typename BinaryOp>
58T reduce(ExecutionPolicy &&policy, R &&r, T init, BinaryOp &&binary_op) {
59
60 static_assert(
61 std::is_same_v<std::remove_cvref_t<ExecutionPolicy>, device_policy>);
62
63 if constexpr (std::is_same_v<std::remove_cvref_t<ExecutionPolicy>,
64 device_policy>) {
65 using future_t = decltype(oneapi::dpl::experimental::reduce_async(
66 __detail::dpl_policy(0), dr::ranges::segments(r)[0].begin(),
67 dr::ranges::segments(r)[0].end(), init, binary_op));
68
69 std::vector<future_t> futures;
70
71 for (auto &&segment : dr::ranges::segments(r)) {
72 auto &&local_policy = __detail::dpl_policy(dr::ranges::rank(segment));
73
74 auto dist = rng::distance(segment);
75 if (dist <= 0) {
76 continue;
77 } else if (dist == 1) {
78 init = binary_op(init, *rng::begin(segment));
79 continue;
80 }
81
82 auto future = reduce_no_init_async<T>(local_policy, rng::begin(segment),
83 rng::end(segment), binary_op);
84
85 futures.push_back(std::move(future));
86 }
87
88 for (auto &&f : futures) {
89 init = binary_op(init, f.get());
90 }
91
92 return init;
93 } else {
94 assert(false);
95 }
96}
97
98template <typename ExecutionPolicy, dr::distributed_range R, typename T>
99T reduce(ExecutionPolicy &&policy, R &&r, T init) {
100 return reduce(std::forward<ExecutionPolicy>(policy), std::forward<R>(r), init,
101 std::plus<>());
102}
103
104template <typename ExecutionPolicy, dr::distributed_range R>
105rng::range_value_t<R> reduce(ExecutionPolicy &&policy, R &&r) {
106 return reduce(std::forward<ExecutionPolicy>(policy), std::forward<R>(r),
107 rng::range_value_t<R>{}, std::plus<>());
108}
109
110// Iterator versions
111
112template <typename ExecutionPolicy, dr::distributed_iterator Iter>
113std::iter_value_t<Iter> reduce(ExecutionPolicy &&policy, Iter first,
114 Iter last) {
115 return reduce(std::forward<ExecutionPolicy>(policy),
116 rng::subrange(first, last), std::iter_value_t<Iter>{},
117 std::plus<>());
118}
119
120template <typename ExecutionPolicy, dr::distributed_iterator Iter, typename T>
121T reduce(ExecutionPolicy &&policy, Iter first, Iter last, T init) {
122 return reduce(std::forward<ExecutionPolicy>(policy),
123 rng::subrange(first, last), init, std::plus<>());
124}
125
126template <typename ExecutionPolicy, dr::distributed_iterator Iter, typename T,
127 typename BinaryOp>
128T reduce(ExecutionPolicy &&policy, Iter first, Iter last, T init,
129 BinaryOp &&binary_op) {
130 return reduce(std::forward<ExecutionPolicy>(policy),
131 rng::subrange(first, last), init,
132 std::forward<BinaryOp>(binary_op));
133}
134
135// Execution policy-less algorithms
136
137template <dr::distributed_range R> rng::range_value_t<R> reduce(R &&r) {
138 return reduce(dr::sp::par_unseq, std::forward<R>(r));
139}
140
141template <dr::distributed_range R, typename T> T reduce(R &&r, T init) {
142 return reduce(dr::sp::par_unseq, std::forward<R>(r), init);
143}
144
145template <dr::distributed_range R, typename T, typename BinaryOp>
146T reduce(R &&r, T init, BinaryOp &&binary_op) {
147 return reduce(dr::sp::par_unseq, std::forward<R>(r), init,
148 std::forward<BinaryOp>(binary_op));
149}
150
151template <dr::distributed_iterator Iter>
152std::iter_value_t<Iter> reduce(Iter first, Iter last) {
153 return reduce(dr::sp::par_unseq, first, last);
154}
155
156template <dr::distributed_iterator Iter, typename T>
157T reduce(Iter first, Iter last, T init) {
158 return reduce(dr::sp::par_unseq, first, last, init);
159}
160
161template <dr::distributed_iterator Iter, typename T, typename BinaryOp>
162T reduce(Iter first, Iter last, T init, BinaryOp &&binary_op) {
163 return reduce(dr::sp::par_unseq, first, last, init,
164 std::forward<BinaryOp>(binary_op));
165}
166
167} // namespace dr::sp
Definition: onedpl_direct_iterator.hpp:15
Definition: concepts.hpp:31
Definition: concepts.hpp:20