iceberg-cpp
Loading...
Searching...
No Matches
executor_util_internal.h
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#pragma once
21
22#include <concepts>
23#include <functional>
24#include <iterator>
25#include <ranges>
26#include <tuple>
27#include <type_traits>
28#include <unordered_map>
29#include <unordered_set>
30#include <utility>
31#include <vector>
32
33#include "iceberg/result.h"
34#include "iceberg/util/executor.h"
35#include "iceberg/util/macros.h"
36#include "iceberg/util/task_group.h"
37
38namespace iceberg {
39
40template <typename T, auto... Options>
42
43namespace internal {
44
45template <typename Ref>
46using ParallelCollectArgT =
47 std::conditional_t<std::is_lvalue_reference_v<Ref>, Ref, std::remove_cvref_t<Ref>&&>;
48
49template <typename T, auto... Options>
50concept ParallelReducible = requires(std::vector<T>& values) {
51 typename ParallelReduce<T, Options...>::result_type;
52 {
53 ParallelReduce<T, Options...>::Reduce(values)
54 } -> std::same_as<typename ParallelReduce<T, Options...>::result_type>;
55};
56
57template <std::ranges::input_range InputRange, typename Task>
58using ParallelCollectValueT = ResultValueT<std::invoke_result_t<
59 std::remove_reference_t<Task>&,
60 ParallelCollectArgT<std::ranges::range_reference_t<InputRange>>>>;
61
62template <std::size_t I, typename... Args>
64 using args_tuple_type = std::tuple<Args&&...>;
65 using input_type = std::tuple_element_t<I * 2, args_tuple_type>;
66 using task_type = std::tuple_element_t<I * 2 + 1, args_tuple_type>;
67 using value_type = ParallelCollectValueT<input_type, task_type>;
68};
69
70template <typename InputRange, typename Task, auto... Options>
72 std::ranges::forward_range<InputRange> && std::ranges::sized_range<InputRange> &&
73 (std::is_lvalue_reference_v<std::ranges::range_reference_t<InputRange>> ||
74 std::constructible_from<
75 std::remove_cvref_t<std::ranges::range_reference_t<InputRange>>,
76 std::ranges::range_reference_t<InputRange>>) &&
77 requires(std::remove_reference_t<Task>& task,
78 ParallelCollectArgT<std::ranges::range_reference_t<InputRange>> item) {
79 { std::invoke(task, std::forward<decltype(item)>(item)) } -> AsResult;
80 requires(!std::same_as<void, ParallelCollectValueT<InputRange, Task>>);
81 requires std::default_initializable<ParallelCollectValueT<InputRange, Task>>;
83 };
84
85} // namespace internal
86
87template <typename... Args>
88struct ParallelReduce<std::unordered_set<Args...>> {
89 using result_type = std::unordered_set<Args...>;
90
91 template <std::ranges::input_range Values>
92 static result_type Reduce(Values&& values) {
93 result_type result;
94 for (auto&& value : values) {
95 result.merge(value);
96 }
97 return result;
98 }
99};
100
101template <typename... Args>
102struct ParallelReduce<std::vector<Args...>> {
103 using result_type = std::vector<Args...>;
104
105 template <std::ranges::input_range Values>
106 static result_type Reduce(Values&& values) {
107 return std::forward<Values>(values) | std::views::join | std::views::as_rvalue |
108 std::ranges::to<result_type>();
109 }
110};
111
112template <typename K, typename... VectorArgs, typename... MapArgs>
113struct ParallelReduce<std::unordered_map<K, std::vector<VectorArgs...>, MapArgs...>> {
114 using result_type = std::unordered_map<K, std::vector<VectorArgs...>, MapArgs...>;
115
116 template <std::ranges::input_range Values>
117 static result_type Reduce(Values&& values) {
118 result_type result;
119 for (auto&& value : values) {
120 result.merge(value);
121 for (auto& [key, entries] : value) {
122 auto& out = result[key];
123 out.insert(out.end(), std::make_move_iterator(entries.begin()),
124 std::make_move_iterator(entries.end()));
125 }
126 }
127 return result;
128 }
129};
130
131template <typename First, typename Second>
132struct ParallelReduce<std::pair<First, Second>> {
133 using result_type = std::pair<typename ParallelReduce<First>::result_type,
135
136 template <std::ranges::forward_range Values>
137 static result_type Reduce(Values&& values) {
138 return {ParallelReduce<First>::Reduce(values | std::views::elements<0>),
139 ParallelReduce<Second>::Reduce(values | std::views::elements<1>)};
140 }
141};
142
143template <typename... Ts>
144struct ParallelReduce<std::tuple<Ts...>> {
145 using result_type = std::tuple<typename ParallelReduce<Ts>::result_type...>;
146
147 template <std::ranges::forward_range Values>
148 static result_type Reduce(Values&& values) {
149 return Reduce(values, std::index_sequence_for<Ts...>{});
150 }
151
152 private:
153 template <std::ranges::forward_range Values, std::size_t... I>
154 static result_type Reduce(Values&& values, std::index_sequence<I...>) {
155 return result_type{ParallelReduce<std::tuple_element_t<I, std::tuple<Ts...>>>::Reduce(
156 values | std::views::elements<I>)...};
157 }
158};
159
160template <auto... Options, typename... Args>
161 requires(sizeof...(Args) >= 2 && sizeof...(Args) % 2 == 0 &&
162 []<std::size_t... I>(std::index_sequence<I...>) consteval {
164 typename internal::ParallelCollectTraits<I, Args...>::input_type,
165 typename internal::ParallelCollectTraits<I, Args...>::task_type,
166 Options...> &&
167 ...);
168 }(std::make_index_sequence<sizeof...(Args) / 2>{}))
169auto ParallelCollect(OptionalExecutor executor, Args&&... args) {
170 constexpr std::size_t pair_count = sizeof...(Args) / 2;
171 using indices = std::make_index_sequence<pair_count>;
172
173 auto args_tuple = std::forward_as_tuple(std::forward<Args>(args)...);
174
175 auto values_tuple = [&]<std::size_t... I>(std::index_sequence<I...>) {
176 return std::tuple{[&] {
177 using traits = internal::ParallelCollectTraits<I, Args...>;
178
179 return std::vector<typename traits::value_type>(
180 std::ranges::size(std::get<I * 2>(args_tuple)));
181 }()...};
182 }(indices{});
183
184 auto reduce_all = [&]<std::size_t... I>(std::index_sequence<I...>) {
185 auto reduce_one = [&]<std::size_t PairIndex> {
186 using traits = internal::ParallelCollectTraits<PairIndex, Args...>;
187 using value_type = typename traits::value_type;
188 return ParallelReduce<value_type, Options...>::Reduce(
189 std::get<PairIndex>(values_tuple));
190 };
191
192 if constexpr (pair_count == 1) {
193 return reduce_one.template operator()<0>();
194 } else {
195 return std::tuple{reduce_one.template operator()<I>()...};
196 }
197 };
198
199 using result_type = decltype(reduce_all(indices{}));
200
201 TaskGroup group;
202 group.SetExecutor(executor);
203
204 [&]<std::size_t... I>(std::index_sequence<I...>) {
205 (
206 [&] {
207 using item_ref = std::ranges::range_reference_t<
208 typename internal::ParallelCollectTraits<I, Args...>::input_type>;
209
210 for (auto&& [item, value] :
211 std::views::zip(std::get<I * 2>(args_tuple), std::get<I>(values_tuple))) {
212 if constexpr (std::is_lvalue_reference_v<item_ref>) {
213 group.Submit([&]() -> Status {
214 ICEBERG_ASSIGN_OR_RAISE(
215 value, std::invoke(std::get<I * 2 + 1>(args_tuple), item));
216 return {};
217 });
218 } else {
219 group.Submit([&, item = std::move(item)]() mutable -> Status {
220 ICEBERG_ASSIGN_OR_RAISE(
221 value, std::invoke(std::get<I * 2 + 1>(args_tuple), std::move(item)));
222 return {};
223 });
224 }
225 }
226 }(),
227 ...);
228 }(indices{});
229
230 auto status = std::move(group).Run();
231 if (!status.has_value()) {
232 return Result<result_type>(std::unexpected<Error>(status.error()));
233 }
234
235 return Result<result_type>(reduce_all(indices{}));
236}
237
238} // namespace iceberg
Definition result.h:140
Definition executor_util_internal.h:71
Definition executor_util_internal.h:50
STL namespace.
Definition executor_util_internal.h:41
Definition executor_util_internal.h:63