Itoyori  v0.0.1
parallel_search.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "ityr/common/util.hpp"
6 
7 namespace ityr {
8 
9 namespace internal {
10 
11 template <typename W, typename LeafOp, typename SelectOp, typename ForwardIterator>
12 inline auto search_aux(const execution::parallel_policy<W>& policy,
13  LeafOp leaf_op,
14  SelectOp select_op,
15  ForwardIterator first,
16  ForwardIterator last) {
17  if constexpr (ori::is_global_ptr_v<ForwardIterator>) {
18  return search_aux(
19  policy,
20  leaf_op,
21  select_op,
22  convert_to_global_iterator(first, checkout_mode::read),
23  convert_to_global_iterator(last , checkout_mode::read));
24 
25  } else {
26  std::size_t d = std::distance(first, last);
27 
28  if (d <= policy.cutoff_count) {
29  auto [css, its] = checkout_global_iterators(d, first);
30  auto first_ = std::get<0>(its);
31  // TODO: `ret` will be a sort of cache of the elements, but it requires copy operations.
32  // Heavy elements (e.g., vectors) might prefer passing by reference (iterator) rather than
33  // passing by value.
34  auto [ret, found_its_] = leaf_op(first_, std::next(first_, d));
35  auto found_its = std::apply([&](auto&&... its_) {
36  return std::make_tuple((first + std::distance(first_, its_))...);
37  }, found_its_);
38  return std::make_tuple(ret, found_its);
39 
40  } else {
41  auto mid = std::next(first, d / 2);
42 
43  auto [r1, r2] = parallel_invoke(
44  [=] { return search_aux(policy, leaf_op, select_op, first, mid); },
45  [=] { return search_aux(policy, leaf_op, select_op, mid, last); });
46 
47  return select_op(r1, r2);
48  }
49  }
50 }
51 
52 }
53 
87 template <typename ExecutionPolicy, typename ForwardIterator, typename Compare>
88 inline ForwardIterator min_element(const ExecutionPolicy& policy,
89  ForwardIterator first,
90  ForwardIterator last,
91  Compare comp) {
92  if (std::distance(first, last) <= 1) {
93  return first;
94  }
95 
96  using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
97 
98  auto leaf_op = [=](const auto& f, const auto& l) {
99  auto it = std::min_element(f, l, comp);
100  if constexpr (std::is_trivially_copyable_v<value_type>) {
101  return std::make_tuple(*it, std::make_tuple(it));
102  } else {
103  return std::make_tuple(std::monostate{}, std::make_tuple(it));
104  }
105  };
106 
107  auto select_op = [=](const auto& l, const auto& r) {
108  auto [val_l, its_l] = l;
109  auto [val_r, its_r] = r;
110  if constexpr (std::is_trivially_copyable_v<value_type>) {
111  return comp(val_r, val_l) ? r : l;
112  } else {
113  auto [it_l] = its_l;
114  auto [it_r] = its_r;
115  auto [css, its] = internal::checkout_global_iterators(1, it_l, it_r);
116  auto [it_l_, it_r_] = its;
117  return comp(*it_r_, *it_l_) ? r : l;
118  }
119  };
120 
121  auto [val, its] = internal::search_aux(policy, leaf_op, select_op, first, last);
122  auto [it] = its;
123  return it;
124 }
125 
150 template <typename ExecutionPolicy, typename ForwardIterator>
151 inline ForwardIterator min_element(const ExecutionPolicy& policy,
152  ForwardIterator first,
153  ForwardIterator last) {
154  return min_element(policy, first, last, std::less<>{});
155 }
156 
190 template <typename ExecutionPolicy, typename ForwardIterator, typename Compare>
191 inline ForwardIterator max_element(const ExecutionPolicy& policy,
192  ForwardIterator first,
193  ForwardIterator last,
194  Compare comp) {
195  if (std::distance(first, last) <= 1) {
196  return first;
197  }
198 
199  using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
200 
201  auto leaf_op = [=](const auto& f, const auto& l) {
202  auto it = std::max_element(f, l, comp);
203  if constexpr (std::is_trivially_copyable_v<value_type>) {
204  return std::make_tuple(*it, std::make_tuple(it));
205  } else {
206  return std::make_tuple(std::monostate{}, std::make_tuple(it));
207  }
208  };
209 
210  auto select_op = [=](const auto& l, const auto& r) {
211  auto [val_l, its_l] = l;
212  auto [val_r, its_r] = r;
213  if constexpr (std::is_trivially_copyable_v<value_type>) {
214  return comp(val_l, val_r) ? r : l;
215  } else {
216  auto [it_l] = its_l;
217  auto [it_r] = its_r;
218  auto [css, its] = internal::checkout_global_iterators(1, it_l, it_r);
219  auto [it_l_, it_r_] = its;
220  return comp(*it_l_, *it_r_) ? r : l;
221  }
222  };
223 
224  auto [val, its] = internal::search_aux(policy, leaf_op, select_op, first, last);
225  auto [it] = its;
226  return it;
227 }
228 
253 template <typename ExecutionPolicy, typename ForwardIterator>
254 inline ForwardIterator max_element(const ExecutionPolicy& policy,
255  ForwardIterator first,
256  ForwardIterator last) {
257  return max_element(policy, first, last, std::less<>{});
258 }
259 
295 template <typename ExecutionPolicy, typename ForwardIterator, typename Compare>
296 inline std::pair<ForwardIterator, ForwardIterator>
297 minmax_element(const ExecutionPolicy& policy,
298  ForwardIterator first,
299  ForwardIterator last,
300  Compare comp) {
301  if (std::distance(first, last) <= 1) {
302  return std::make_pair(first, first);
303  }
304 
305  using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
306 
307  auto leaf_op = [=](const auto& f, const auto& l) {
308  auto [min_it, max_it] = std::minmax_element(f, l, comp);
309  if constexpr (std::is_trivially_copyable_v<value_type>) {
310  return std::make_tuple(
311  std::make_pair(*min_it, *max_it), std::make_tuple(min_it, max_it));
312  } else {
313  return std::make_tuple(
314  std::monostate{}, std::make_tuple(min_it, max_it));
315  }
316  };
317 
318  auto select_op = [=](const auto& l, const auto& r) {
319  auto [vals_l, its_l] = l;
320  auto [vals_r, its_r] = r;
321  auto [min_it_l, max_it_l] = its_l;
322  auto [min_it_r, max_it_r] = its_r;
323 
324  if constexpr (std::is_trivially_copyable_v<value_type>) {
325  auto [min_val_l, max_val_l] = vals_l;
326  auto [min_val_r, max_val_r] = vals_r;
327 
328  decltype(min_val_l) min_val = min_val_l;
329  decltype(min_it_l) min_it = min_it_l;
330  if (comp(min_val_r, min_val_l)) {
331  min_val = min_val_r;
332  min_it = min_it_r;
333  }
334 
335  decltype(max_val_l) max_val = max_val_l;
336  decltype(max_it_l) max_it = max_it_l;
337  if (comp(max_val_l, max_val_r)) {
338  max_val = max_val_r;
339  max_it = max_it_r;
340  }
341 
342  return std::make_tuple(
343  std::make_pair(min_val, max_val), std::make_tuple(min_it, max_it));
344 
345  } else {
346  auto [css, its] = internal::checkout_global_iterators(
347  1, min_it_l, max_it_l, min_it_r, max_it_r);
348  auto [min_it_l_, max_it_l_, min_it_r_, max_it_r_] = its;
349 
350  auto min_it = comp(*min_it_r_, *min_it_l_) ? min_it_r : min_it_l;
351  auto max_it = comp(*max_it_l_, *max_it_r_) ? max_it_r : max_it_l;
352  return std::make_tuple(
353  std::monostate{}, std::make_tuple(min_it, max_it));
354  }
355  };
356 
357  auto [vals, its] = internal::search_aux(policy, leaf_op, select_op, first, last);
358  auto [min_it, max_it] = its;
359  return std::make_pair(min_it, max_it);
360 }
361 
387 template <typename ExecutionPolicy, typename ForwardIterator>
388 inline std::pair<ForwardIterator, ForwardIterator>
389 minmax_element(const ExecutionPolicy& policy,
390  ForwardIterator first,
391  ForwardIterator last) {
392  return minmax_element(policy, first, last, std::less<>{});
393 }
394 
395 ITYR_TEST_CASE("[ityr::pattern::parallel_search] min, max, minmax_element") {
396  ito::init();
397  ori::init();
398 
399  long n = 100000;
400  ori::global_ptr<long> p = ori::malloc_coll<long>(n);
401 
402  ito::root_exec([=] {
403  transform(
404  execution::parallel_policy(100),
405  count_iterator<long>(0), count_iterator<long>(n), p,
406  [=](long i) { return (3 * i + 5) % 13; });
407 
408  long max_val = 14;
409  long max_pos = n / 3;
410  long max_pos_dummy = n / 3 * 2;
411  p[max_pos].put(max_val);
412  p[max_pos_dummy].put(max_val);
413 
414  long min_val = -1;
415  long min_pos = n / 4;
416  long min_pos_dummy = n - 1;
417  p[min_pos].put(min_val);
418  p[min_pos_dummy].put(min_val);
419 
420  auto max_it = max_element(execution::parallel_policy(100), p, p + n);
421  ITYR_CHECK(max_it == p + max_pos);
422  ITYR_CHECK((*max_it).get() == max_val);
423 
424  auto min_it = min_element(execution::parallel_policy(100), p, p + n);
425  ITYR_CHECK(min_it == p + min_pos);
426  ITYR_CHECK((*min_it).get() == min_val);
427 
428  auto [min_it2, max_it2] = minmax_element(execution::parallel_policy(100), p, p + n);
429  ITYR_CHECK(min_it2 == p + min_pos);
430  ITYR_CHECK((*min_it2).get() == min_val);
431  ITYR_CHECK(max_it2 == p + max_pos);
432  ITYR_CHECK((*max_it2).get() == max_val);
433  });
434 
435  ori::free_coll(p);
436 
437  ori::fini();
438  ito::fini();
439 }
440 
441 }
#define ITYR_CHECK(cond)
Definition: util.hpp:48
constexpr read_t read
Read-only checkout mode.
Definition: checkout_span.hpp:19
void fini()
Definition: ito.hpp:45
auto root_exec(Fn &&fn, Args &&... args)
Definition: ito.hpp:50
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ito.hpp:41
void fini()
Definition: ori.hpp:49
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ori.hpp:45
void free_coll(global_ptr< T > ptr)
Definition: ori.hpp:70
Definition: allocator.hpp:16
ForwardIterator max_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last, Compare comp)
Search for the maximum element in a range.
Definition: parallel_search.hpp:191
std::pair< ForwardIterator, ForwardIterator > minmax_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last, Compare comp)
Search for the minimum and maximum element in a range.
Definition: parallel_search.hpp:297
auto parallel_invoke(Args &&... args)
Fork parallel tasks and join them.
Definition: parallel_invoke.hpp:238
ForwardIteratorD transform(const ExecutionPolicy &policy, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIteratorD first_d, UnaryOp unary_op)
Transform elements in a given range and store them in another range.
Definition: parallel_loop.hpp:583
ForwardIterator min_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last, Compare comp)
Search for the minimum element in a range.
Definition: parallel_search.hpp:88
ForwardIterator min_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last)
Search for the minimum element in a range.
Definition: parallel_search.hpp:151
ForwardIterator max_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last)
Search for the maximum element in a range.
Definition: parallel_search.hpp:254
std::pair< ForwardIterator, ForwardIterator > minmax_element(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last)
Search for the minimum and maximum element in a range.
Definition: parallel_search.hpp:389