KaMPIng 0.2.0
Flexible and (near) zero-overhead C++ bindings for MPI
Loading...
Searching...
No Matches
scan.hpp
1
2// This file is part of KaMPIng.
3//
4// Copyright 2022-2024 The KaMPIng Authors
5//
6// KaMPIng is free software : you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
7// License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
8// version. KaMPIng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
9// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
10// for more details.
11//
12// You should have received a copy of the GNU Lesser General Public License along with KaMPIng. If not, see
13// <https://www.gnu.org/licenses/>.
14
15#pragma once
16
17#include <mpi.h>
18
19#include "kamping/assertion_levels.hpp"
21#include "kamping/collectives/collectives_helpers.hpp"
22#include "kamping/comm_helper/is_same_on_all_ranks.hpp"
23#include "kamping/communicator.hpp"
26#include "kamping/kassert/kassert.hpp"
32#include "kamping/result.hpp"
33
34/// @addtogroup kamping_collectives
35/// @{
36
37/// @brief Wrapper for \c MPI_Scan.
38///
39/// This wraps \c MPI_Scan, which is used to perform an inclusive prefix reduction on data distributed across the
40/// calling processes. \c scan() returns in \c recv_buf of the process with rank \c i, the reduction (calculated
41/// according to the function op) of the values in the sendbufs of processes with ranks \f$0, ..., i\f$ (inclusive).
42///
43/// The following parameters are required:
44/// - \ref kamping::send_buf() containing the data for which to perform the scan. This buffer has to be the
45/// same size at each rank.
46///
47/// - \ref kamping::op() wrapping the operation to apply to the input. If \ref kamping::send_recv_type() is provided
48/// explicitly, the compatibility of the type and operation has to be ensured by the user.
49///
50/// The following parameters are optional:
51/// - \ref kamping::recv_buf() containing a buffer for the output. A buffer size of at least `send_recv_count` elements
52/// is required.
53///
54/// - \ref kamping::send_recv_count() containing the number of elements to be processed in this operation. This
55/// parameter has to be the same at each rank. If omitted, the size of the send buffer will be used as
56/// `send_recv_count`.
57///
58/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as data type in this operation. If omitted,
59/// the \c MPI datatype is derived automatically based on send_buf's underlying \c value_type.
60///
61/// In-place scan is supported by providing `send_recv_buf()` instead of `send_buf()` and `recv_buf()`. For details
62/// on the in-place version, see \ref Communicator::scan_inplace().
63///
64/// @tparam Args Automatically deduced template parameters.
65/// @param args All required and any number of the optional parameters described above.
66/// @return Result object wrapping the output parameters to be returned by value.
67///
68/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
69/// <hr>
70/// \include{doc} docs/resize_policy.dox
71template <
72 template <typename...>
73 typename DefaultContainerType,
74 template <typename, template <typename...> typename>
75 typename... Plugins>
76template <typename... Args>
78 using namespace kamping::internal;
80 if constexpr (inplace) {
81 return this->scan_inplace(std::forward<Args>(args)...);
82 } else {
84 Args,
87 );
88
89 // get the send buffer and deduce the send and recv value types.
90 auto const send_buf = select_parameter_type<ParameterType::send_buf>(args...).construct_buffer_or_rebind();
91 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
92 using default_recv_value_type = std::remove_const_t<send_value_type>;
93
94 // deduce the recv buffer type and get (if provided) the recv buffer or allocate one (if not provided).
97 auto recv_buf =
100
101 // get the send_recv_type
103 [[maybe_unused]] constexpr bool send_recv_type_is_in_param = !has_to_be_computed<decltype(send_recv_type)>;
104
105 // get the send_recv count
109 default_send_recv_count_type>(std::tuple<>(), args...)
110 .construct_buffer_or_rebind();
111
113 if constexpr (do_compute_send_recv_count) {
114 send_recv_count.underlying() = asserting_cast<int>(send_buf.size());
115 }
116
118 is_same_on_all_ranks(send_recv_count.get_single_element()),
119 "The send_recv_count has to be the same on all ranks.",
121 );
122
123 // get the operation used for the reduction. The signature of the provided function is checked while building.
126
127 auto compute_required_recv_buf_size = [&]() {
128 return asserting_cast<size_t>(send_recv_count.get_single_element());
129 };
130 recv_buf.resize_if_requested(compute_required_recv_buf_size);
132 // if the send_recv type is user provided, kamping cannot make any assumptions about the required size of
133 // the recv buffer
135 "Recv buffer is not large enough to hold all received elements.",
137 );
138
139 // Perform the MPI_Scan call and return.
140 [[maybe_unused]] int err = MPI_Scan(
141 send_buf.data(), // sendbuf
142 recv_buf.data(), // recvbuf,
143 send_recv_count.get_single_element(), // count
144 send_recv_type.get_single_element(), // datatype,
145 operation.op(), // op
146 mpi_communicator() // communicator
147 );
148
149 this->mpi_error_hook(err, "MPI_Scan");
150 return make_mpi_result<std::tuple<Args...>>(
151 std::move(recv_buf),
152 std::move(send_recv_type),
153 std::move(send_recv_count)
154 );
155 }
156}
157
158/// @brief Wrapper for the in-place version of \ref Communicator::scan().
159///
160/// This variant must be called collectively by all ranks in the communicator. It is semantically equivalent to \ref
161/// Communicator::scan(), but the input buffer is used as the output buffer. This means that the input buffer is
162/// overwritten with the result of the scan.
163///
164/// The following parameters are required:
165/// - \ref kamping::send_recv_buf() containing the data for which to perform the scan and will store the result
166/// of the scan.
167///
168/// - \ref kamping::op() wrapping the operation to apply to the input. If \ref kamping::send_recv_type() is provided
169/// explicitly, the compatibility of the type and operation has to be ensured by the user.
170///
171/// The following parameters are optional:
172/// - \ref kamping::send_recv_count() containing the number of elements to be processed in this operation. This
173/// parameter has to be the same at each rank. If omitted, the size of the send buffer will be used as send_recv_count.
174///
175/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as data type in this operation. If omitted,
176/// the \c MPI datatype is derived automatically based on send_recv_buf's underlying \c value_type.
177///
178/// @tparam Args Automatically deduced template parameters.
179/// @param args All required and any number of the optional parameters described above.
180/// @return Result object wrapping the output parameters to be returned by value.
181///
182/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
183/// <hr>
184/// \include{doc} docs/resize_policy.dox
185template <
186 template <typename...>
187 typename DefaultContainerType,
188 template <typename, template <typename...> typename>
189 typename... Plugins>
190template <typename... Args>
192 using namespace kamping::internal;
194 Args,
197 );
198
199 // get the send recv buffer and deduce the send and recv value types.
200 auto send_recv_buf = select_parameter_type<ParameterType::send_recv_buf>(args...).construct_buffer_or_rebind();
201 using value_type = typename std::remove_reference_t<decltype(send_recv_buf)>::value_type;
202
203 // get the send_recv_type
205 [[maybe_unused]] constexpr bool type_is_in_param = !has_to_be_computed<decltype(type)>;
206
207 // get the send_recv count
209 auto count =
210 internal::select_parameter_type_or_default<internal::ParameterType::send_recv_count, default_count_type>(
211 std::tuple<>(),
212 args...
213 )
214 .construct_buffer_or_rebind();
215
216 constexpr bool do_compute_count = internal::has_to_be_computed<decltype(count)>;
217 if constexpr (do_compute_count) {
218 count.underlying() = asserting_cast<int>(send_recv_buf.size());
219 }
220
222 is_same_on_all_ranks(count.get_single_element()),
223 "The send_recv_count has to be the same on all ranks.",
225 );
226
227 // get the operation used for the reduction. The signature of the provided function is checked while building.
230
231 auto compute_required_recv_buf_size = [&]() {
232 return asserting_cast<size_t>(count.get_single_element());
233 };
236 // if the send_recv type is user provided, kamping cannot make any assumptions about the required size of
237 // the buffer
239 "Send/Recv buffer is not large enough to hold all received elements.",
241 );
242
243 // Perform the MPI_Scan call and return.
244 [[maybe_unused]] int err = MPI_Scan(
245 MPI_IN_PLACE, // sendbuf
246 send_recv_buf.data(), // recvbuf,
247 count.get_single_element(), // count
248 type.get_single_element(), // datatype,
249 operation.op(), // op
250 mpi_communicator() // communicator
251 );
252
253 this->mpi_error_hook(err, "MPI_Scan");
254 return make_mpi_result<std::tuple<Args...>>(std::move(send_recv_buf), std::move(type), std::move(count));
255}
256
257/// @brief Wrapper for \c MPI_Scan for single elements.
258///
259/// This is functionally equivalent to \c scan() but provided for uniformity with other operations (e.g. \c
260/// bcast_single()). \c scan_single() wraps \c MPI_Scan, which is used to perform an inclusive prefix reduction on data
261/// distributed across the calling processes. \c scan() returns on the process with rank \f$i\f$, the
262/// reduction (calculated according to the function op) of the values in the sendbufs of processes with ranks \f$0, ...,
263/// i\f$ (inclusive).
264///
265/// The following parameters are required:
266/// - kamping::send_buf() containing the data for which to perform the scan. This buffer has to be a single element on
267/// each rank.
268/// - kamping::op() wrapping the operation to apply to the input.
269///
270/// @tparam Args Automatically deduced template parameters.
271/// @param args All required and any number of the optional buffers described above.
272/// @return The single element result of the scan.
273template <
274 template <typename...>
275 typename DefaultContainerType,
276 template <typename, template <typename...> typename>
277 typename... Plugins>
278template <typename... Args>
280 //! If you expand this function to not being only a simple wrapper around scan, you have to write more unit
281 //! tests!
282
283 using namespace kamping::internal;
284
285 // The send and recv buffers are always of the same size in scan, thus, there is no additional exchange of
286 // recv_counts.
288
289 using send_buf_type = buffer_type_with_requested_parameter_type<ParameterType::send_buf, Args...>;
290 static_assert(
291 send_buf_type::is_single_element,
292 "The underlying container has to be a single element \"container\""
293 );
294 using value_type = typename send_buf_type::value_type;
295 return this->scan(recv_buf(alloc_new<value_type>), std::forward<Args>(args)...);
296}
297/// @}
Helper functions that make casts safer.
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
T value_type
The value type.
Definition allocator.hpp:53
Code for error handling.
constexpr int light
Assertion level for lightweight assertions.
Definition assertion_levels.hpp:13
constexpr int light_communication
Assertions that perform lightweight communication.
Definition assertion_levels.hpp:25
auto scan_single(Args... args) const
Wrapper for MPI_Scan for single elements.
Definition scan.hpp:279
auto scan_inplace(Args... args) const
Wrapper for the in-place version of Communicator::scan().
Definition scan.hpp:191
auto scan(Args... args) const
Wrapper for MPI_Scan.
Definition scan.hpp:77
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:195
auto send_recv_buf(Data &&data)
Passes a container/single value as a send or receive buffer to the underlying MPI call.
Definition named_parameters.hpp:139
auto send_recv_count_out()
Indicates to deduce the send/recv count and return it to the caller as part of the underlying call's ...
Definition named_parameters.hpp:557
auto send_recv_type(MPI_Datatype send_recv_type)
Passes send_recv_type as send/recv type to the underlying call. (This parameter is in MPI routines su...
Definition named_parameters.hpp:1346
auto recv_buf(Container &&container)
Passes a container, into which the received elements will be written, to the underlying call....
Definition named_parameters.hpp:861
internal::OperationBuilder< Op, Commutative > op(Op &&op, Commutative commute=ops::internal::undefined_commutative_tag{})
Passes a reduction operation to ther underlying call. Accepts function objects, lambdas,...
Definition named_parameters.hpp:1219
auto send_recv_count(int count)
Passes count as send/recv count to the underlying call.
Definition named_parameters.hpp:531
auto send_buf(internal::ignore_t< Data > ignore)
Generates a dummy send buf that wraps a nullptr.
Definition named_parameters.hpp:53
decltype(auto) select_parameter_type_or_default(std::tuple< DefaultArguments... > default_arguments, Args &... args)
Checks if parameter with requested parameter type exists, if not constructs a default value.
Definition named_parameter_selection.hpp:238
std:: tuple_element_t< find_pos< std::integral_constant< ParameterType, parameter_type >, 0, Args... >(), std::tuple< Args... > > buffer_type_with_requested_parameter_type
Type of Buffer with requested.
Definition named_parameter_selection.hpp:175
constexpr bool has_parameter_type()
Checks if parameter with requested parameter type exists.
Definition named_parameter_selection.hpp:185
Utility that maps C++ types to types that can be understood by MPI.
Template magic to check named parameters passed to wrappers at compile time.
#define KAMPING_REQUIRED_PARAMETERS(...)
Wrapper to pass (possibly empty) list of parameter type names as required parameters to KAMPING_CHECK...
Definition named_parameter_check.hpp:52
#define KAMPING_OPTIONAL_PARAMETERS(...)
Wrapper to pass (possibly empty) list of parameter type names as optional parameters to KAMPING_CHECK...
Definition named_parameter_check.hpp:58
#define KAMPING_CHECK_PARAMETERS(args, required, optional)
Assertion macro that checks if passed parameters are correct, i.e., all parameter types are unique,...
Definition named_parameter_check.hpp:80
Template magic to implement named parameters in cpp.
File containing the parameter types used by the KaMPIng library.
Factory methods for buffer wrappers.
Internal namespace marking the code that is not user-facing.
Definition collectives_helpers.hpp:20
static constexpr bool has_to_be_computed
Checks if the buffer has to be computed by kamping, i.e. if it is an output parameter or the buffer h...
Definition named_parameter_check.hpp:398
Some functions and types simplifying/enabling the development of wrapped MPI calls in KaMPIng.