Reduction.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <charm++.h>
7 #include <tuple>
8 
9 #include "Parallel/CharmRegistration.hpp"
11 #include "Parallel/PupStlCpp11.hpp"
13 #include "Utilities/Functional.hpp"
14 #include "Utilities/Gsl.hpp"
15 #include "Utilities/Requires.hpp"
16 #include "Utilities/TypeTraits.hpp"
17 
18 namespace Parallel {
19 /// \cond
20 template <class... Ts>
21 struct ReductionData;
22 /// \endcond
23 
24 namespace detail {
25 /*!
26  * \ingroup ParallelGroup
27  * \brief Convert a `ReductionData` to a `CkReductionMsg`. Used in custom
28  * reducers.
29  */
30 template <class... Ts>
31 CkReductionMsg* new_reduction_msg(
32  ReductionData<Ts...>& reduction_data) noexcept {
33  return CkReductionMsg::buildNew(static_cast<int>(reduction_data.size()),
34  reduction_data.packed().get());
35 }
36 } // namespace detail
37 
38 /*!
39  * \ingroup ParallelGroup
40  * \brief The data to be reduced, and invokables to be called whenever two
41  * reduction messages are combined and after the reduction has been completed.
42  *
43  * `InvokeCombine` is a binary invokable that maps `(T current_state, T element)
44  * -> T`, where the `current_state` is the result of reductions so far. The
45  * `InvokeFinal` is an n-ary that takes as its first argument a `T
46  * result_of_reduction` and is invoked once after the reduction is completed.
47  * The additional arguments correspond to the resultant data of earlier
48  * `ReductionDatum` template parameters in the `ReductionData`, and are
49  * identified via the `InvokeFinalExtraArgsIndices`, which must be a
50  * `std::index_sequence`. Specifically, say you want the third
51  * `ReductionDatum`'s `InvokeFinal` to be passed the first `ReductionDatum` then
52  * `std::index_sequence<0>` would be passed for `InvokeFinalExtraArgsIndices`.
53  * Here is an example of computing the RMS error of the evolved variables `u`
54  * and `v`:
55  *
56  * \snippet Test_AlgorithmReduction.cpp contribute_to_rms_reduction
57  *
58  * with the receiving action:
59  *
60  * \snippet Test_AlgorithmReduction.cpp reduce_rms_action
61  */
62 template <class T, class InvokeCombine, class InvokeFinal = funcl::Identity,
63  class InvokeFinalExtraArgsIndices = std::index_sequence<>>
65  using value_type = T;
66  using invoke_combine = InvokeCombine;
67  using invoke_final = InvokeFinal;
68  using invoke_final_extra_args_indices = InvokeFinalExtraArgsIndices;
69  T value;
70 };
71 
72 /*!
73  * \ingroup ParallelGroup
74  * \brief Used for reducing a possibly heterogeneous collection of types in a
75  * single reduction call
76  */
77 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
78  class... InvokeFinalExtraArgsIndices>
79 struct ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
80  InvokeFinalExtraArgsIndices>...> {
81  static_assert(sizeof...(Ts) > 0,
82  "Must be reducing at least one piece of data.");
83  static constexpr size_t pack_size() noexcept { return sizeof...(Ts); }
84  using datum_list = tmpl::list<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
85  InvokeFinalExtraArgsIndices>...>;
86 
87  explicit ReductionData(
88  ReductionDatum<Ts, InvokeCombines, InvokeFinals,
89  InvokeFinalExtraArgsIndices>... args) noexcept;
90 
91  explicit ReductionData(Ts... args) noexcept;
92 
93  ReductionData() = default;
94  ReductionData(const ReductionData& /*rhs*/) = default;
95  ReductionData& operator=(const ReductionData& /*rhs*/) = default;
96  ReductionData(ReductionData&& /*rhs*/) = default;
97  ReductionData& operator=(ReductionData&& /*rhs*/) = default;
98  ~ReductionData() = default;
99 
100  explicit ReductionData(CkReductionMsg* const message) noexcept {
101  PUP::fromMem creator(message->getData());
102  creator | *this;
103  }
104 
105  static CkReductionMsg* combine(int number_of_messages,
106  CkReductionMsg** msgs) noexcept;
107 
108  ReductionData& combine(ReductionData&& t) noexcept {
109  ReductionData::combine_helper(this, std::move(t),
110  std::make_index_sequence<sizeof...(Ts)>{});
111  return *this;
112  }
113 
114  ReductionData& finalize() noexcept {
115  invoke_final_loop_over_tuple(std::make_index_sequence<sizeof...(Ts)>{});
116  return *this;
117  }
118 
119  /// \cond
120  // clang-tidy: non-const reference
121  void pup(PUP::er& p) noexcept { p | data_; } // NOLINT
122 
123  std::unique_ptr<char[]> packed() noexcept;
124 
125  size_t size() noexcept;
126 
127  const std::tuple<Ts...>& data() const noexcept { return data_; }
128 
129  std::tuple<Ts...>& data() noexcept { return data_; }
130 
131  private:
132  template <size_t... Is>
133  static void combine_helper(gsl::not_null<ReductionData*> reduced,
134  ReductionData&& current,
135  std::index_sequence<Is...> /*meta*/) noexcept;
136 
137  template <size_t I, class InvokeFinal, size_t... Js>
138  void invoke_final_helper(std::index_sequence<Js...> /*meta*/) noexcept;
139 
140  template <size_t... Is>
141  void invoke_final_loop_over_tuple(
142  std::index_sequence<Is...> /*meta*/) noexcept;
143 
144  std::tuple<Ts...> data_;
145  /// \endcond
146 };
147 
148 /// \cond
149 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
150  class... InvokeFinalExtraArgsIndices>
151 ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
152  InvokeFinalExtraArgsIndices>...>::
153  ReductionData(ReductionDatum<Ts, InvokeCombines, InvokeFinals,
154  InvokeFinalExtraArgsIndices>... args) noexcept
155  : data_(std::move(args.value)...) {}
156 
157 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
158  class... InvokeFinalExtraArgsIndices>
159 ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
160  InvokeFinalExtraArgsIndices>...>::
161  ReductionData(Ts... args) noexcept
162  : data_(std::move(args)...) {}
163 
164 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
165  class... InvokeFinalExtraArgsIndices>
166 CkReductionMsg* ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
167  InvokeFinalExtraArgsIndices>...>::
168  combine(const int number_of_messages,
169  CkReductionMsg** const msgs) noexcept {
170  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
171  ReductionData reduced(msgs[0]);
172  for (int msg_id = 1; msg_id < number_of_messages; ++msg_id) {
173  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
174  ReductionData current(msgs[msg_id]);
175  ReductionData::combine_helper(&reduced, std::move(current),
176  std::make_index_sequence<sizeof...(Ts)>{});
177  }
178  return detail::new_reduction_msg(reduced);
179 }
180 
181 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
182  class... InvokeFinalExtraArgsIndices>
183 std::unique_ptr<char[]> ReductionData<
184  ReductionDatum<Ts, InvokeCombines, InvokeFinals,
185  InvokeFinalExtraArgsIndices>...>::packed() noexcept {
186  auto result = std::make_unique<char[]>(size());
187  PUP::toMem packer(result.get());
188  packer | *this;
189  return result;
190 }
191 
192 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
193  class... InvokeFinalExtraArgsIndices>
194 size_t
195 ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
196  InvokeFinalExtraArgsIndices>...>::size() noexcept {
197  PUP::sizer size_pup;
198  size_pup | *this;
199  return size_pup.size();
200 }
201 
202 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
203  class... InvokeFinalExtraArgsIndices>
204 template <size_t... Is>
205 void ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
206  InvokeFinalExtraArgsIndices>...>::
207  combine_helper(const gsl::not_null<ReductionData*> reduced,
208  ReductionData&& current,
209  std::index_sequence<Is...> /*meta*/) noexcept {
210  EXPAND_PACK_LEFT_TO_RIGHT((std::get<Is>(reduced->data_) = InvokeCombines{}(
211  std::move(std::get<Is>(reduced->data_)),
212  std::move(std::get<Is>(current.data_)))));
213 }
214 
215 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
216  class... InvokeFinalExtraArgsIndices>
217 template <size_t I, class InvokeFinal, size_t... Js>
218 void ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
219  InvokeFinalExtraArgsIndices>...>::
220  invoke_final_helper(std::index_sequence<Js...> /*meta*/) noexcept {
221  std::get<I>(data_) = InvokeFinal{}(std::move(std::get<I>(data_)),
222  cpp17::as_const(std::get<Js>(data_))...);
223 }
224 
225 template <class... Ts, class... InvokeCombines, class... InvokeFinals,
226  class... InvokeFinalExtraArgsIndices>
227 template <size_t... Is>
228 void ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,
229  InvokeFinalExtraArgsIndices>...>::
230  invoke_final_loop_over_tuple(std::index_sequence<Is...> /*meta*/) noexcept {
232  invoke_final_helper<Is, InvokeFinals>(InvokeFinalExtraArgsIndices{}));
233 }
234 /// \endcond
235 
236 /*!
237  * \ingroup ParallelGroup
238  * \brief Perform a reduction from the `sender_component` (typically your own
239  * parallel component) to the `target_component`, performing the `Action` upon
240  * receiving the reduction.
241  */
242 template <class Action, class SenderProxy, class TargetProxy, class... Ts>
243 void contribute_to_reduction(ReductionData<Ts...> reduction_data,
244  const SenderProxy& sender_component,
245  const TargetProxy& target_component) noexcept {
247  &ReductionData<Ts...>::combine>::registrar;
248  CkCallback callback(
249  TargetProxy::index_t::template redn_wrapper_reduction_action<
250  Action, std::decay_t<ReductionData<Ts...>>>(nullptr),
251  target_component);
252  sender_component.ckLocal()->contribute(
253  static_cast<int>(reduction_data.size()), reduction_data.packed().get(),
254  Parallel::charmxx::charm_reducer_functions.at(
256  &ReductionData<Ts...>::combine)),
257  callback);
258 }
259 } // namespace Parallel
#define EXPAND_PACK_LEFT_TO_RIGHT(...)
Expand a parameter pack evaluating the terms from left to right.
Definition: TMPL.hpp:561
The identity higher order function object.
Definition: Functional.hpp:214
constexpr const T & as_const(const T &t) noexcept
Returns a const reference to its argument.
Definition: ConstantExpressions.hpp:408
void contribute_to_reduction(ReductionData< Ts... > reduction_data, const SenderProxy &sender_component, const TargetProxy &target_component) noexcept
Perform a reduction from the sender_component (typically your own parallel component) to the target_c...
Definition: Reduction.hpp:243
Contains functions that forward to Charm++ parallel functions.
Definition: Abort.hpp:13
Defines the type alias Requires.
Definition: Determinant.hpp:11
PUP routines for new C+11 STL containers and other standard library objects Charm does not provide im...
Define simple functions for constant expressions.
Defines functions and classes from the GSL.
Class used for registering custom reduction function.
Definition: CharmRegistration.hpp:622
Defines class template ConstGlobalCache.
Defines type traits, some of which are future STL type_traits header.
The data to be reduced, and invokables to be called whenever two reduction messages are combined and ...
Definition: Reduction.hpp:64
Require a pointer to not be a nullptr
Definition: ConservativeFromPrimitive.hpp:12