KaMPIng 0.1.2
Flexible and (near) zero-overhead C++ bindings for MPI
Loading...
Searching...
No Matches
sendrecv.hpp
1// This file is part of KaMPIng.
2//
3// Copyright 2022-2025 The KaMPIng Authors
4//
5// KaMPIng is free software : you can redistribute it and/or modify it under the
6// terms of the GNU Lesser General Public License as published by the Free
7// Software Foundation, either version 3 of the License, or (at your option) any
8// later version. KaMPIng is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11// for more details.
12//
13// You should have received a copy of the GNU Lesser General Public License
14// along with KaMPIng. If not, see <https://www.gnu.org/licenses/>.
15
16#pragma once
17
18#include <type_traits>
19#include <utility>
20
21#include <kassert/kassert.hpp>
22#include <mpi.h>
23
24#include "kamping/communicator.hpp"
26#include "kamping/implementation_helpers.hpp"
31#include "kamping/p2p/helpers.hpp"
32#include "kamping/p2p/probe.hpp"
34#include "kamping/result.hpp"
35#include "kamping/status.hpp"
36
37///// @addtogroup kamping_p2p
38/// @{
39
40// @brief Wrapper for \c MPI_Sendrecv.
41///
42/// This wraps \c MPI_Sendrecv. This operation performs a blocking send and receive operation. If the
43/// \ref kamping::recv_counts() parameter is not specified, this first performs a sendrecv, sending and receiving
44/// the recv count.
45///
46/// The following parameters are required:
47/// - \ref kamping::send_buf() containing the data that is sent.
48///
49///
50/// - \ref kamping::destination() the receiving rank.
51///
52/// The following parameter is optional, but leads to an additional call to \c MPI_Sendrecv if not present:
53/// - \ref kamping::recv_count() the number of elements to receive. Will be calculated using an additional sendrecv
54/// if not given. Note that this additional sendrecv can be received by \c send(). This will lead to unintended
55/// behavior, if \c sendrecv() is used in combination with \c send() or \c recv() while omitting
56/// the \ref kamping::recv_counts() parameter.
57///
58/// The following parameters are optional:
59/// - \ref kamping::recv_buf() the buffer to receive the message into. The buffer's underlying
60/// storage must be large enough to hold all received elements. If no \ref kamping::recv_buf() is provided, the \c
61/// value_type of the recv buffer has to be passed as a template parameter to \c sendrecv().
62///
63/// - \ref kamping::send_count() specifying how many elements of the buffer are sent. If omitted, the size of the send
64/// buffer is used as a default.
65///
66/// - \ref kamping::source() receive a message sent from this source rank. Defaults to probing for an arbitrary source,
67/// i.e. \c source(rank::any).
68///
69/// - \ref kamping::send_tag() send message with this tag. Defaults to the communicator's default tag (\ref
70///// Communicator::default_tag()) if not present.
71///
72/// - \ref kamping::recv_tag() receive message with this tag. Defaults to receiving for an arbitrary tag, i.e. \c
73/// tag(tags::any).
74///
75/// - \c kamping::status(ignore<>) or \ref kamping::status_out(). Returns info about the received message by setting the
76/// appropriate fields in the status object passed by the user. If \ref kamping::status_out() is passed, constructs a
77/// status object which may be retrieved by the user. The status can be ignored by passing \c
78/// kamping::status(kamping::ignore<>). This is the default.
79///
80/// @tparam Args Automatically deduced template parameters.
81/// @tparam recv_value_type_tparam The type of the message to be received.
82/// @param args All required and any number of the optional parameters described above.
83/// @return Result object wrapping the output parameters to be returned by value.
84///
85/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
86/// <hr>
87
88template <
89 template <typename...>
90 typename DefaultContainerType,
91 template <typename, template <typename...> typename>
92 typename... Plugins>
93template <typename recv_value_type_tparam /* = kamping::internal::unused_tparam */, typename... Args>
95 using namespace kamping::internal;
97 Args,
101 send_type,
102 send_tag,
103 recv_buf,
104 recv_tag,
105 source,
106 recv_type,
107 status,
109 )
110 );
111
112 auto send_buf = internal::select_parameter_type<internal::ParameterType::send_buf>(args...)
114
116 auto send_count =
117 internal::select_parameter_type_or_default<internal::ParameterType::send_count, default_send_count_type>(
118 {},
119 args...
120 )
121 .construct_buffer_or_rebind();
122 if constexpr (has_to_be_computed<decltype(send_count)>) {
123 send_count.underlying() = asserting_cast<int>(send_buf.size());
124 }
125
126 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
127 auto send_type = internal::determine_mpi_send_datatype<send_value_type>(args...);
128
129 auto const& destination = internal::select_parameter_type<internal::ParameterType::destination>(args...);
130 constexpr auto rank_type = std::remove_reference_t<decltype(destination)>::rank_type;
131 static_assert(
132 rank_type == RankType::value || rank_type == RankType::null,
133 "Please provide an explicit destination or destination(ranks::null)."
134 );
135
136 using default_send_tag_type = decltype(kamping::send_tag(this->default_tag()));
137 auto&& send_tag_param =
138 internal::select_parameter_type_or_default<internal::ParameterType::send_tag, default_send_tag_type>(
139 std::tuple(this->default_tag()),
140 args...
141 );
142
143 // this ensures that the user does not try to pass MPI_ANY_TAG, which is not allowed for the send tag
144 static_assert(
145 std::remove_reference_t<decltype(send_tag_param)>::tag_type == TagType::value,
146 "Please provide a send tag for the message."
147 );
148 int send_tag = send_tag_param.tag();
149 KASSERT(
151 "invalid send tag " << send_tag << ", must be in range [0, " << Environment<>::tag_upper_bound() << "]"
152 );
153
155 auto recv_buf =
156 internal::select_parameter_type_or_default<internal::ParameterType::recv_buf, default_recv_buf_type>(
157 std::tuple<>(),
158 args...
159 )
161
163 auto recv_count_param =
164 internal::select_parameter_type_or_default<internal::ParameterType::recv_count, default_recv_count_type>(
165 std::tuple<>(),
166 args...
167 )
168 .construct_buffer_or_rebind();
169
170 using recv_value_type = typename std::remove_reference_t<decltype(recv_buf)>::value_type;
171 static_assert(
172 !std::is_same_v<recv_value_type, internal::unused_tparam>,
173 "No recv_buf parameter provided and no receive value given as template parameter. One of these is required."
174 );
175 auto recv_type = internal::determine_mpi_recv_datatype<recv_value_type, decltype(recv_buf)>(args...);
177
178 using default_recv_tag_type = decltype(kamping::recv_tag(tags::any));
179 auto&& recv_tag_param =
180 internal::select_parameter_type_or_default<internal::ParameterType::recv_tag, default_recv_tag_type>(
181 {},
182 args...
183 );
184 constexpr auto tag_type = std::remove_reference_t<decltype(recv_tag_param)>::tag_type;
185 if constexpr (tag_type == internal::TagType::value) {
186 int recv_tag = recv_tag_param.tag();
187 KASSERT(
189 "invalid recv tag " << recv_tag << ", must be in range [0, " << Environment<>::tag_upper_bound() << "]"
190 );
191 }
192
194 auto status =
195 internal::select_parameter_type_or_default<internal::ParameterType::status, default_status_param_type>(
196 {},
197 args...
198 )
199 .construct_buffer_or_rebind();
200
201 using default_source_buf_type = decltype(kamping::source(rank::any));
202 auto&& source_param =
203 internal::select_parameter_type_or_default<internal::ParameterType::source, default_source_buf_type>(
204 {},
205 args...
206 );
207
209 int source = source_param.rank_signed();
210
211 // Calculate rev_count if not given by calling sendrecv with the send_count
212 if constexpr (internal::has_to_be_computed<decltype(recv_count_param)>) {
213 this->sendrecv(
214 kamping::destination(destination.rank_signed()),
215 std::move(source_param),
217 kamping::send_buf(send_count.get_single_element()),
220 );
221 }
222
223 // Ensure that we do not touch the recv buffer if MPI_PROC_NULL is passed,
224 // because this is what the standard guarantees.
225 // Resize the buffer if requested and ensure that it is large enough
226 if constexpr (std::remove_reference_t<decltype(source_param)>::rank_type != internal::RankType::null) {
228 return asserting_cast<size_t>(recv_count_param.get_single_element());
229 };
230 recv_buf.resize_if_requested(compute_required_recv_buf_size);
231 KASSERT(
232 // if the recv type is user provided, kamping cannot make any assumptions about the required size of the
233 // recv buffer
235 "Recv buffer is not large enough to hold all received elements.",
237 );
238 }
239
240 [[maybe_unused]] int err = MPI_Sendrecv(
241 send_buf.data(), // send_buff
242 send_count.get_single_element(), // send_count
243 send_type.get_single_element(), // send_data_type
244 destination.rank_signed(), // destination
245 send_tag, // send_tag
246 recv_buf.data(), // recv_buff
247 recv_count_param.get_single_element(), // recv_count
248 recv_type.get_single_element(), // recv_data_type
249 source, // source
250 recv_tag_param.tag(), // recv_tag
251 this->mpi_communicator(), // comm
253 );
254 this->mpi_error_hook(err, "MPI_Sendrecv");
255
256 return internal::make_mpi_result<std::tuple<Args...>>(
257 std::move(recv_buf),
258 std::move(recv_count_param),
259 std::move(status),
260 std::move(recv_type)
261 );
262}
263/// @}
Wrapper for MPI functions that don't require a communicator. If the template parameter init_finalize_...
Definition environment.hpp:52
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
constexpr int light
Assertion level for lightweight assertions.
Definition assertion_levels.hpp:13
auto status(internal::ignore_t< void >)
pass MPI_STATUS_IGNORE to the underlying MPI call.
Definition status_parameters.hpp:54
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:194
auto send_tag(internal::any_tag_t)
Indicates to use MPI_ANY_TAG as send tag in the underlying call.
Definition named_parameters.hpp:1097
auto source(int rank)
Passes rank as source rank to the underlying call. This parameter is needed in point-to-point exchang...
Definition named_parameters.hpp:1030
auto send_count(int count)
Passes count as send count to the underlying call.
Definition named_parameters.hpp:323
auto recv_count(int count)
Passes count as recv count to the underlying call.
Definition named_parameters.hpp:492
auto destination(int rank)
Passes rank as destination rank to the underlying call. This parameter is needed in point-to-point ex...
Definition named_parameters.hpp:1001
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
auto recv_type(MPI_Datatype recv_type)
Passes recv_type as recv type to the underlying call.
Definition named_parameters.hpp:1302
auto send_type(MPI_Datatype send_type)
Passes send_type as send type to the underlying call.
Definition named_parameters.hpp:1259
auto send_count_out()
Indicates to deduce the send count and return it to the caller as part of the underlying call's resul...
Definition named_parameters.hpp:349
auto recv_count_out()
Indicates to deduce the recv count and return it to the caller as part of the underlying call's resul...
Definition named_parameters.hpp:518
auto recv_tag(internal::any_tag_t)
Indicates to use MPI_ANY_TAG as recv tag in the underlying call.
Definition named_parameters.hpp:1128
auto send_buf(internal::ignore_t< Data > ignore)
Generates a dummy send buf that wraps a nullptr.
Definition named_parameters.hpp:53
auto sendrecv(Args... args) const
Definition sendrecv.hpp:94
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
@ value
holds an actual value
@ null
holds MPI_PROC_NULL
constexpr bool is_valid_rank_in_comm(RankDataBufferClass const &rank_data_buffer, Comm const &comm, bool const allow_null=false, bool const allow_any=false)
Checks whether a RankDataBuffer contains a valid rank in the given communicator.
Definition implementation_helpers.hpp:30
auto make_mpi_result(Buffers &&... buffers)
Construct result object for a wrapped MPI call. Four different cases are handled: a) The recv_buffer ...
Definition result.hpp:1019
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
static MPI_Status * status_param_to_native_ptr(StatusParam &param)
returns a pointer to the MPI_Status encapsulated by the provided status parameter object.
Definition parameter_objects.hpp:489
Parameter objects return by named parameter factory functions.
Some functions and types simplifying/enabling the development of wrapped MPI calls in KaMPIng.