KaMPIng 0.1.0
(Near) zero-overhead C++ MPI bindings.
Loading...
Searching...
No Matches
reduce.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_Reduce.
40///
41/// This wraps \c MPI_Reduce. The operation combines the elements in the input buffer provided via \c
42/// kamping::send_buf() and returns the combined value on the root rank.
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 be the same size at
46/// each rank.
47///
48/// - \ref kamping::recv_buf() specifying a buffer for the output. This parameter is only required on the root rank.
49///
50/// - \ref kamping::op() wrapping the operation to apply to the input. If \ref kamping::send_recv_type() is provided,
51/// the compatibility of the type and operation has to be ensured by the user.
52///
53/// The following parameters are optional:
54/// - \ref kamping::send_recv_count() specifying how many elements of the buffer take part in the reduction.
55/// If omitted, the size of the send buffer is used as a default. This parameter is mandatory if \ref
56/// kamping::send_type() is given.
57///
58/// - \ref kamping::send_recv_type() specifying the \c MPI datatype to use as send_recv type. If omitted, the \c MPI
59/// datatype is derived automatically based on send_buf's underlying \c value_type.
60///
61/// - \ref kamping::root() the root rank. If not set, the default root process of the communicator will be used.
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 Args,
82 );
83
84 // Get the root
85 auto&& root = internal::select_parameter_type_or_default<internal::ParameterType::root, internal::RootDataBuffer>(
86 std::tuple(this->root()),
87 args...
88 );
89
90 // Get the send buffer and deduce the send and recv value types.
91 auto const& send_buf =
92 internal::select_parameter_type<internal::ParameterType::send_buf>(args...).construct_buffer_or_rebind();
93 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
94 using default_recv_value_type = std::remove_const_t<send_value_type>;
95
97 auto&& recv_buf =
98 internal::select_parameter_type_or_default<internal::ParameterType::recv_buf, default_recv_buf_type>(
99 std::tuple(),
100 args...
101 )
103
104 // Get the send 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.
109 auto& operation_param = internal::select_parameter_type<internal::ParameterType::op>(args...);
110 // If you want to understand the syntax of the following line, ignore the "template " ;-)
112
117 .construct_buffer_or_rebind();
118 if constexpr (has_to_be_computed<decltype(send_recv_count)>) {
119 send_recv_count.underlying() = asserting_cast<int>(send_buf.size());
120 }
121
122 // Check parameters
123
124 // from the standard:
125 // > The routine is called by all group members using the same arguments for count, datatype, op,
126 // > root and comm.
127 KASSERT(
128 this->is_same_on_all_ranks(send_recv_count.get_single_element()),
129 "send_recv_count() has to be the same on all ranks.",
131 );
132 KASSERT(is_valid_rank(root.rank_signed()), "The provided root rank is invalid.", assert::light);
133 KASSERT(
134 this->is_same_on_all_ranks(root.rank_signed()),
135 "Root has to be the same on all ranks.",
137 );
138
139 if (is_root(root.rank_signed())) {
141 return asserting_cast<size_t>(send_recv_count.get_single_element());
142 };
143 recv_buf.resize_if_requested(compute_required_recv_buf_size);
144 KASSERT(
145 // if the send type is user provided, kamping cannot make any assumptions about the required size of the
146 // recv buffer
148 "Recv buffer is not large enough to hold all received elements.",
150 );
151 }
152
153 [[maybe_unused]] int err = MPI_Reduce(
154 send_buf.data(), // send_buf
155 recv_buf.data(), // recv_buf
156 send_recv_count.get_single_element(), // count
157 send_recv_type.get_single_element(), // type
158 operation.op(), // op
159 root.rank_signed(), // root
160 mpi_communicator() // comm
161 );
162
163 this->mpi_error_hook(err, "MPI_Reduce");
164 return make_mpi_result<std::tuple<Args...>>(
165 std::move(recv_buf),
166 std::move(send_recv_count),
167 std::move(send_recv_type)
168 );
169}
170
171/// @brief Wrapper for \c MPI_Reduce.
172///
173/// Calling reduce_single() is a shorthand for calling reduce() with a \ref kamping::send_buf() of size 1. It
174/// always issues only a single <code>MPI_Reduce</code> call, as no receive counts have to be exchanged.
175///
176/// The following parameters are required:
177/// - \ref kamping::send_buf() containing the data that is sent to each rank. This buffer has to wrap a single element
178/// on each rank.
179/// - \ref kamping::op() wrapping the operation to apply to the input.
180///
181/// The following parameters are optional:
182/// - \ref kamping::root() the root rank. If not set, the default root process of the communicator will be used.
183///
184/// @tparam Args Automatically deduced template parameters.
185/// @param args All required and any number of the optional buffers described above.
186/// @return Returns an std::optional object encapsulating the reduced value on the root rank and an empty std::optional
187/// object on all non-root ranks.
188template <
189 template <typename...>
190 typename DefaultContainerType,
191 template <typename, template <typename...> typename>
192 typename... Plugins>
193template <typename... Args>
195 using namespace kamping::internal;
197
198 using send_buf_type = buffer_type_with_requested_parameter_type<ParameterType::send_buf, Args...>;
199 static_assert(
200 send_buf_type::is_single_element,
201 "The underlying container has to be a single element \"container\""
202 );
203
204 auto&& root = internal::select_parameter_type_or_default<internal::ParameterType::root, internal::RootDataBuffer>(
205 std::tuple(this->root()),
206 args...
207 );
208
209 using value_type = typename std::remove_reference_t<
210 decltype(select_parameter_type<ParameterType::send_buf>(args...).construct_buffer_or_rebind())>::value_type;
211
212 if (is_root(root.rank_signed())) {
213 return std::optional<value_type>{this->reduce(recv_buf(alloc_new<value_type>), std::forward<Args>(args)...)};
214 } else {
215 this->reduce(std::forward<Args>(args)...);
216 return std::optional<value_type>{};
217 }
218}
219/// @}
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 reduce(Args... args) const
Definition reduce.hpp:76
auto reduce_single(Args... args) const
Wrapper for MPI_Reduce.
Definition reduce.hpp:194
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:170
auto root(int rank)
Passes rank as root rank to the underlying call. This parameter is needed in functions like MPI_Gathe...
Definition named_parameters.hpp:979
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_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
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.