KaMPIng 0.1.0
(Near) zero-overhead C++ MPI bindings.
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 <kassert/kassert.hpp>
18#include <mpi.h>
19
20#include "kamping/assertion_levels.hpp"
22#include "kamping/collectives/collectives_helpers.hpp"
23#include "kamping/comm_helper/is_same_on_all_ranks.hpp"
24#include "kamping/communicator.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 send_recv_count.
56///
57/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as data type in this operation. If omitted,
58/// the \c MPI datatype is derived automatically based on send_buf's underlying \c value_type.
59///
60/// In-place scan is supported by providing `send_recv_buf()` instead of `send_buf()` and `recv_buf()`. For details
61/// on the in-place version, see \ref Communicator::scan_inplace().
62///
63/// @tparam Args Automatically deduced template parameters.
64/// @param args All required and any number of the optional parameters described above.
65/// @return Result object wrapping the output parameters to be returned by value.
66///
67/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
68/// <hr>
69/// \include{doc} docs/resize_policy.dox
70template <
71 template <typename...>
72 typename DefaultContainerType,
73 template <typename, template <typename...> typename>
74 typename... Plugins>
75template <typename... Args>
77 using namespace kamping::internal;
79 if constexpr (inplace) {
80 return this->scan_inplace(std::forward<Args>(args)...);
81 } else {
83 Args,
86 );
87
88 // get the send buffer and deduce the send and recv value types.
89 auto const&& send_buf = select_parameter_type<ParameterType::send_buf>(args...).construct_buffer_or_rebind();
90 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
91 using default_recv_value_type = std::remove_const_t<send_value_type>;
92
93 // deduce the recv buffer type and get (if provided) the recv buffer or allocate one (if not provided).
96 auto&& recv_buf =
99
100 // get the send_recv_type
102 [[maybe_unused]] constexpr bool send_recv_type_is_in_param = !has_to_be_computed<decltype(send_recv_type)>;
103
104 // get the send_recv count
108 default_send_recv_count_type>(std::tuple(), args...)
109 .construct_buffer_or_rebind();
110
112 if constexpr (do_compute_send_recv_count) {
113 send_recv_count.underlying() = asserting_cast<int>(send_buf.size());
114 }
115
116 KASSERT(
117 is_same_on_all_ranks(send_recv_count.get_single_element()),
118 "The send_recv_count has to be the same on all ranks.",
120 );
121
122 // get the operation used for the reduction. The signature of the provided function is checked while building.
125
126 auto compute_required_recv_buf_size = [&]() {
127 return asserting_cast<size_t>(send_recv_count.get_single_element());
128 };
129 recv_buf.resize_if_requested(compute_required_recv_buf_size);
130 KASSERT(
131 // if the send_recv type is user provided, kamping cannot make any assumptions about the required size of
132 // the recv buffer
134 "Recv buffer is not large enough to hold all received elements.",
136 );
137
138 // Perform the MPI_Scan call and return.
139 [[maybe_unused]] int err = MPI_Scan(
140 send_buf.data(), // sendbuf
141 recv_buf.data(), // recvbuf,
142 send_recv_count.get_single_element(), // count
143 send_recv_type.get_single_element(), // datatype,
144 operation.op(), // op
145 mpi_communicator() // communicator
146 );
147
148 this->mpi_error_hook(err, "MPI_Scan");
149 return make_mpi_result<std::tuple<Args...>>(
150 std::move(recv_buf),
151 std::move(send_recv_type),
152 std::move(send_recv_count)
153 );
154 }
155}
156
157/// @brief Wrapper for the in-place version of \ref Communicator::scan().
158///
159/// This variant must be called collectively by all ranks in the communicator. It is semantically equivalent to \ref
160/// Communicator::scan(), but the input buffer is used as the output buffer. This means that the input buffer is
161/// overwritten with the result of the scan.
162///
163/// The following parameters are required:
164/// - \ref kamping::send_recv_buf() containing the data for which to perform the scan and will store the result
165/// of the scan.
166///
167/// - \ref kamping::op() wrapping the operation to apply to the input. If \ref kamping::send_recv_type() is provided
168/// explicitly, the compatibility of the type and operation has to be ensured by the user.
169///
170/// The following parameters are optional:
171/// - \ref kamping::send_recv_count() containing the number of elements to be processed in this operation. This
172/// parameter has to be the same at each rank. If omitted, the size of the send buffer will be used as send_recv_count.
173///
174/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as data type in this operation. If omitted,
175/// the \c MPI datatype is derived automatically based on send_recv_buf's underlying \c value_type.
176///
177/// @tparam Args Automatically deduced template parameters.
178/// @param args All required and any number of the optional parameters described above.
179/// @return Result object wrapping the output parameters to be returned by value.
180///
181/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
182/// <hr>
183/// \include{doc} docs/resize_policy.dox
184template <
185 template <typename...>
186 typename DefaultContainerType,
187 template <typename, template <typename...> typename>
188 typename... Plugins>
189template <typename... Args>
191 using namespace kamping::internal;
193 Args,
196 );
197
198 // get the send recv buffer and deduce the send and recv value types.
199 auto&& send_recv_buf = select_parameter_type<ParameterType::send_recv_buf>(args...).construct_buffer_or_rebind();
200 using value_type = typename std::remove_reference_t<decltype(send_recv_buf)>::value_type;
201
202 // get the send_recv_type
204 [[maybe_unused]] constexpr bool type_is_in_param = !has_to_be_computed<decltype(type)>;
205
206 // get the send_recv count
208 auto&& count =
209 internal::select_parameter_type_or_default<internal::ParameterType::send_recv_count, default_count_type>(
210 std::tuple(),
211 args...
212 )
213 .construct_buffer_or_rebind();
214
215 constexpr bool do_compute_count = internal::has_to_be_computed<decltype(count)>;
216 if constexpr (do_compute_count) {
217 count.underlying() = asserting_cast<int>(send_recv_buf.size());
218 }
219
220 KASSERT(
221 is_same_on_all_ranks(count.get_single_element()),
222 "The send_recv_count has to be the same on all ranks.",
224 );
225
226 // get the operation used for the reduction. The signature of the provided function is checked while building.
229
230 auto compute_required_recv_buf_size = [&]() {
231 return asserting_cast<size_t>(count.get_single_element());
232 };
234 KASSERT(
235 // if the send_recv type is user provided, kamping cannot make any assumptions about the required size of
236 // the buffer
238 "Send/Recv buffer is not large enough to hold all received elements.",
240 );
241
242 // Perform the MPI_Scan call and return.
243 [[maybe_unused]] int err = MPI_Scan(
244 MPI_IN_PLACE, // sendbuf
245 send_recv_buf.data(), // recvbuf,
246 count.get_single_element(), // count
247 type.get_single_element(), // datatype,
248 operation.op(), // op
249 mpi_communicator() // communicator
250 );
251
252 this->mpi_error_hook(err, "MPI_Scan");
253 return make_mpi_result<std::tuple<Args...>>(std::move(send_recv_buf), std::move(type), std::move(count));
254}
255
256/// @brief Wrapper for \c MPI_Scan for single elements.
257///
258/// This is functionally equivalent to \c scan() but provided for uniformity with other operations (e.g. \c
259/// bcast_single()). \c scan_single() wraps \c MPI_Scan, which is used to perform an inclusive prefix reduction on data
260/// distributed across the calling processes. \c scan() returns on the process with rank \f$i\f$, the
261/// reduction (calculated according to the function op) of the values in the sendbufs of processes with ranks \f$0, ...,
262/// i\f$ (inclusive).
263///
264/// The following parameters are required:
265/// - kamping::send_buf() containing the data for which to perform the scan. This buffer has to be a single element on
266/// each rank.
267/// - kamping::op() wrapping the operation to apply to the input.
268///
269/// @tparam Args Automatically deduced template parameters.
270/// @param args All required and any number of the optional buffers described above.
271/// @return The single element result of the scan.
272template <
273 template <typename...>
274 typename DefaultContainerType,
275 template <typename, template <typename...> typename>
276 typename... Plugins>
277template <typename... Args>
279 //! If you expand this function to not being only a simple wrapper around scan, you have to write more unit
280 //! tests!
281
282 using namespace kamping::internal;
283
284 // The send and recv buffers are always of the same size in scan, thus, there is no additional exchange of
285 // recv_counts.
287
288 using send_buf_type = buffer_type_with_requested_parameter_type<ParameterType::send_buf, Args...>;
289 static_assert(
290 send_buf_type::is_single_element,
291 "The underlying container has to be a single element \"container\""
292 );
293 using value_type = typename send_buf_type::value_type;
294 return this->scan(recv_buf(alloc_new<value_type>), std::forward<Args>(args)...);
295}
296/// @}
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:278
auto scan_inplace(Args... args) const
Wrapper for the in-place version of Communicator::scan().
Definition scan.hpp:190
auto scan(Args... args) const
Definition scan.hpp:76
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:170
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:1155
auto recv_buf(Container &&container)
Passes a container, into which the received elements will be written, to the underlying call....
Definition named_parameters.hpp:859
auto send_buf(internal::ignore_t< Data > ignore)
Generates a dummy send buf that wraps a nullptr.
Definition named_parameters.hpp:51
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:137
auto send_recv_count(int count)
Passes count as send/recv count to the underlying call.
Definition named_parameters.hpp:529
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:1282
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:555
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:239
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:176
constexpr bool has_parameter_type()
Checks if parameter with requested parameter type exists.
Definition named_parameter_selection.hpp:186
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.