KaMPIng 0.1.1
Flexible and (near) zero-overhead C++ bindings for MPI
Loading...
Searching...
No Matches
allreduce.hpp
1// This file is part of KaMPIng.
2//
3// Copyright 2022-2024 The KaMPIng Authors
4//
5// KaMPIng is free software : you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
7// version. KaMPIng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
9// for more details.
10//
11// You should have received a copy of the GNU Lesser General Public License along with KaMPIng. If not, see
12// <https://www.gnu.org/licenses/>.
13
14#pragma once
15
16#include <tuple>
17#include <type_traits>
18
19#include <kassert/kassert.hpp>
20#include <mpi.h>
21
22#include "kamping/assertion_levels.hpp"
24#include "kamping/collectives/collectives_helpers.hpp"
25#include "kamping/comm_helper/is_same_on_all_ranks.hpp"
26#include "kamping/communicator.hpp"
34#include "kamping/result.hpp"
35
36/// @addtogroup kamping_collectives
37/// @{
38
39/// @brief Wrapper for \c MPI_Allreduce; which is semantically a reduction followed by a broadcast.
40///
41/// This wraps \c MPI_Allreduce. The operation combines the elements in the input buffer provided via \c
42/// kamping::send_buf() and returns the combined value on all ranks.
43///
44/// The following parameters are required:
45/// - \ref kamping::send_buf() containing the data that is sent to each rank. This buffer has to have the same size at
46/// each rank.
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.
52///
53/// - \ref kamping::send_recv_count() specifying how many elements of the send buffer take part in the reduction. If
54/// omitted, the size of send buffer is used. This parameter is mandatory if \ref kamping::send_recv_type() is given.
55///
56/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as send type. If omitted, the \c MPI datatype
57/// is derived automatically based on send_buf's underlying \c value_type.
58///
59/// In-place allreduce is supported by providing `send_recv_buf()` instead of `send_buf()` and `recv_buf()`. For details
60/// on the in-place version, see \ref Communicator::allreduce_inplace().
61///
62/// @tparam Args Automatically deduced template parameters.
63/// @param args All required and any number of the optional parameters described above.
64/// @return Result object wrapping the output parameters to be returned by value.
65///
66/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
67/// <hr>
68/// \include{doc} docs/resize_policy.dox
69template <
70 template <typename...>
71 typename DefaultContainerType,
72 template <typename, template <typename...> typename>
73 typename... Plugins>
74template <typename... Args>
76 using namespace kamping::internal;
78 if constexpr (inplace) {
79 return this->allreduce_inplace(std::forward<Args>(args)...);
80 } else {
82 Args,
85 );
86
87 // Get the send buffer and deduce the send and recv value types.
88 auto const& send_buf = select_parameter_type<ParameterType::send_buf>(args...).construct_buffer_or_rebind();
89 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
90 using default_recv_value_type = std::remove_const_t<send_value_type>;
91
92 // Deduce the recv buffer type and get (if provided) the recv buffer or allocate one (if not provided).
95 auto&& recv_buf =
98 using recv_value_type = typename std::remove_reference_t<decltype(recv_buf)>::value_type;
99 static_assert(
100 std::is_same_v<std::remove_const_t<send_value_type>, recv_value_type>,
101 "Types of send and receive buffers do not match."
102 );
103
104 // Get the send_recv_type.
106 [[maybe_unused]] constexpr bool send_recv_type_is_in_param = !has_to_be_computed<decltype(send_recv_type)>;
107
108 // Get the operation used for the reduction. The signature of the provided function is checked while building.
111
116 .construct_buffer_or_rebind();
117 if constexpr (has_to_be_computed<decltype(send_recv_count)>) {
118 send_recv_count.underlying() = asserting_cast<int>(send_buf.size());
119 }
120 // from the standard:
121 // > The routine is called by all group members using the same arguments for count, datatype, op,
122 // > root and comm.
123 KASSERT(
124 this->is_same_on_all_ranks(send_recv_count.get_single_element()),
125 "send_count() has to be the same on all ranks.",
127 );
128
130 return asserting_cast<size_t>(send_recv_count.get_single_element());
131 };
132 recv_buf.resize_if_requested(compute_required_recv_buf_size);
133
134 KASSERT(
135 // if the send type is user provided, kamping cannot make any assumptions about the required size of the
136 // recv buffer
138 "Recv buffer is not large enough to hold all received elements.",
140 );
141
142 // Perform the MPI_Allreduce call and return.
144 send_buf.data(), // sendbuf
145 recv_buf.data(), // recvbuf,
146 send_recv_count.get_single_element(), // count
147 send_recv_type.get_single_element(), // datatype,
148 operation.op(), // op
149 mpi_communicator() // communicator
150 );
151
152 this->mpi_error_hook(err, "MPI_Allreduce");
153 return make_mpi_result<std::tuple<Args...>>(
154 std::move(recv_buf),
155 std::move(send_recv_count),
156 std::move(send_recv_type)
157 );
158 }
159}
160
161/// @brief Wrapper for the in-place version of \ref Communicator::allreduce().
162///
163/// This variant must be called collectively by all ranks in the communicator. It is semantically equivalent to \ref
164/// Communicator::allreduce(), but the input buffer is used as the output buffer. This means that the input buffer is
165/// overwritten with the result of the reduction.
166///
167/// The following parameters are required:
168/// - \ref kamping::send_recv_buf() containing the data that is sent to each rank and will store the result of the
169/// reduction.
170///
171/// - \ref kamping::op() wrapping the operation to apply to the input. If \ref kamping::send_recv_type() is provided
172/// explicitly, the compatibility of the type and operation has to be ensured by the user.
173///
174/// The following parameters are optional:
175/// - \ref kamping::send_recv_count() specifying how many elements of the send buffer take part in the reduction. If
176/// omitted, the size of `send_recv_buf` is used. This parameter is mandatory if \ref kamping::send_recv_type() is
177/// given.
178///
179/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as send type. If omitted, the \c MPI datatype
180/// is derived automatically based on `send_recv_buf`'s underlying \c value_type.
181///
182/// @tparam Args Automatically deduced template parameters.
183/// @param args All required and any number of the optional parameters described above.
184/// @return Result object wrapping the output parameters to be returned by value.
185///
186/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
187/// <hr>
188/// \include{doc} docs/resize_policy.dox
189template <
190 template <typename...>
191 typename DefaultContainerType,
192 template <typename, template <typename...> typename>
193 typename... Plugins>
194template <typename... Args>
196 using namespace kamping::internal;
198 Args,
201 );
202
203 // Get the send buffer and deduce the send and recv value types.
204 auto&& send_recv_buf = select_parameter_type<ParameterType::send_recv_buf>(args...).construct_buffer_or_rebind();
205 using send_recv_value_type = typename std::remove_reference_t<decltype(send_recv_buf)>::value_type;
206
207 // Get the send_recv_type.
209 [[maybe_unused]] constexpr bool send_recv_type_is_in_param = !has_to_be_computed<decltype(send_recv_type)>;
210
211 // Get the operation used for the reduction. The signature of the provided function is checked while building.
214
219 .construct_buffer_or_rebind();
220 if constexpr (has_to_be_computed<decltype(send_recv_count)>) {
222 }
223 // from the standard:
224 // > The routine is called by all group members using the same arguments for count, datatype, op,
225 // > root and comm.
226 KASSERT(
227 this->is_same_on_all_ranks(send_recv_count.get_single_element()),
228 "send_recv_count() has to be the same on all ranks.",
230 );
231
232 KASSERT(
233 // if the send type is user provided, kamping cannot make any assumptions about the required size of the
234 // recv buffer
236 || send_recv_buf.size() >= asserting_cast<size_t>(send_recv_count.get_single_element()),
237 "Recv buffer is not large enough to hold all received elements.",
239 );
240
241 // Perform the MPI_Allreduce call and return.
243 MPI_IN_PLACE, // sendbuf
244 send_recv_buf.data(), // recvbuf
245 send_recv_count.get_single_element(), // count
246 send_recv_type.get_single_element(), // datatype,
247 operation.op(), // op
248 mpi_communicator() // communicator
249 );
250
251 this->mpi_error_hook(err, "MPI_Allreduce");
252 return make_mpi_result<std::tuple<Args...>>(
253 std::move(send_recv_buf),
254 std::move(send_recv_count),
255 std::move(send_recv_type)
256 );
257}
258
259/// @brief Wrapper for \c MPI_Allreduce; which is semantically a reduction followed by a broadcast.
260///
261/// Calling allreduce_single() is a shorthand for calling allreduce() with a \ref kamping::send_buf() of size 1. It
262/// always issues only a single <code>MPI_Allreduce</code> call, as no receive counts have to be exchanged.
263///
264/// The following parameters are required:
265/// - \ref kamping::send_buf() containing the data that is sent to each rank. This buffer has to wrap a single element
266/// on each rank.
267/// - \ref 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 parameters described above.
271/// @return The single output value.
272template <
273 template <typename...>
274 typename DefaultContainerType,
275 template <typename, template <typename...> typename>
276 typename... Plugins>
277template <typename... Args>
279 using namespace kamping::internal;
281
282 using send_buf_type = buffer_type_with_requested_parameter_type<ParameterType::send_buf, Args...>;
283 static_assert(
284 send_buf_type::is_single_element,
285 "The underlying container has to be a single element \"container\""
286 );
287
288 using value_type = typename std::remove_reference_t<
289 decltype(select_parameter_type<ParameterType::send_buf>(args...).construct_buffer_or_rebind())>::value_type;
290 return this->allreduce(recv_buf(alloc_new<value_type>), std::forward<Args>(args)...);
291}
292/// @}
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 allreduce_inplace(Args... args) const
Wrapper for the in-place version of Communicator::allreduce().
Definition allreduce.hpp:195
auto allreduce(Args... args) const
Wrapper for MPI_Allreduce; which is semantically a reduction followed by a broadcast.
Definition allreduce.hpp:75
auto allreduce_single(Args... args) const
Wrapper for MPI_Allreduce; which is semantically a reduction followed by a broadcast.
Definition allreduce.hpp:278
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:194
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
Some functions and types simplifying/enabling the development of wrapped MPI calls in KaMPIng.