KaMPIng 0.1.1
Flexible and (near) zero-overhead C++ bindings for MPI
Loading...
Searching...
No Matches
alltoall.hpp
1// This file is part of KaMPIng.
2//
3// Copyright 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 <cstddef>
17#include <numeric>
18#include <tuple>
19#include <type_traits>
20
21#include <kassert/kassert.hpp>
22#include <mpi.h>
23
24#include "kamping/assertion_levels.hpp"
26#include "kamping/collectives/barrier.hpp"
27#include "kamping/collectives/collectives_helpers.hpp"
28#include "kamping/collectives/ibarrier.hpp"
29#include "kamping/comm_helper/is_same_on_all_ranks.hpp"
34#include "kamping/p2p/iprobe.hpp"
35#include "kamping/p2p/isend.hpp"
36#include "kamping/p2p/recv.hpp"
37#include "kamping/request_pool.hpp"
38#include "kamping/result.hpp"
39#include "kamping/topology_communicator.hpp"
40
41/// @addtogroup kamping_collectives
42/// @{
43
44/// @brief Wrapper for \c MPI_Neighbor_alltoall.
45///
46/// This wrapper for \c MPI_Neighbor_alltoall sends the same amount of data
47/// from a rank i to each of its neighbour j for which an edge (i,j) in the communication graph exists. The following
48/// buffers are required:
49/// @todo check again once the concrete semantics (potential differing number of send/recv counts) of
50/// MPI_Neighbor_alltoall has been clarified.
51/// - \ref kamping::send_buf() containing the data that is sent to each neighbor. This buffer has to be divisible by the
52/// out degree unless a send_count or a send_type is explicitly given as parameter.
53///
54/// The following parameters are optional:
55/// - \ref kamping::send_count() specifying how many elements are sent. If
56/// omitted, the size of send buffer divided by number of outgoing neighbors is used.
57/// This has to be the same on all ranks.
58/// This parameter is mandatory if \ref kamping::send_type() is given.
59///
60/// - \ref kamping::recv_count() specifying how many elements are received. If
61/// omitted, the value of send_counts will be used.
62/// This parameter is mandatory if \ref kamping::recv_type() is given.
63///
64/// - \ref kamping::recv_buf() specifying a buffer for the output. A buffer of at least
65/// `recv_count * in degree` is required.
66///
67/// - \ref kamping::send_type() specifying the \c MPI datatype to use as send type. If omitted, the \c MPI datatype is
68/// derived automatically based on send_buf's underlying \c value_type.
69///
70/// - \ref kamping::recv_type() specifying the \c MPI datatype to use as recv type. If omitted, the \c MPI datatype is
71/// derived automatically based on recv_buf's underlying \c value_type.
72///
73/// @tparam Args Automatically deduced template parameters.
74/// @param args All required and any number of the optional buffers described above.
75/// @return Result type wrapping the output parameters to be returned by value.
76///
77/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
78/// <hr>
79/// \include{doc} docs/resize_policy.dox
80
81template <
82 template <typename...>
83 typename DefaultContainerType,
84 template <typename, template <typename...> typename>
85 typename... Plugins>
86template <typename... Args>
88 using namespace internal;
90 Args,
93 );
94 // Get the buffers
95 auto const&& send_buf =
96 internal::select_parameter_type<internal::ParameterType::send_buf>(args...).construct_buffer_or_rebind();
97 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
98 using default_recv_value_type = std::remove_const_t<send_value_type>;
99
101 auto&& recv_buf =
102 internal::select_parameter_type_or_default<internal::ParameterType::recv_buf, default_recv_buf_type>(
103 std::tuple(),
104 args...
105 )
107 using recv_value_type = typename std::remove_reference_t<decltype(recv_buf)>::value_type;
108
109 static_assert(!std::is_const_v<recv_value_type>, "The receive buffer must not have a const value_type.");
110
111 auto&& [send_type, recv_type] =
112 internal::determine_mpi_datatypes<send_value_type, recv_value_type, decltype(recv_buf)>(args...);
113 [[maybe_unused]] constexpr bool recv_type_has_to_be_deduced = has_to_be_computed<decltype(recv_type)>;
114
115 // Get the send counts
117 auto&& send_count =
118 internal::select_parameter_type_or_default<internal::ParameterType::send_count, default_send_count_type>(
119 std::tuple(),
120 args...
121 )
122 .construct_buffer_or_rebind();
124 if constexpr (do_compute_send_count) {
125 send_count.underlying() =
126 this->out_degree() == 0 ? 0 : asserting_cast<int>(send_buf.size() / this->out_degree());
127 }
128 // Get the recv counts
130 auto&& recv_count =
131 internal::select_parameter_type_or_default<internal::ParameterType::recv_count, default_recv_count_type>(
132 std::tuple(),
133 args...
134 )
135 .construct_buffer_or_rebind();
136
138 if constexpr (do_compute_recv_count) {
139 recv_count.underlying() = send_count.get_single_element();
140 }
141
142 KASSERT(
143 // @todo check this condition once we know the exact intended semantics of neighbor_alltoall
144 (!do_compute_send_count || this->out_degree() == 0 || send_buf.size() % this->out_degree() == 0lu),
145 "There are no send counts given and the number of elements in send_buf is not divisible by the number "
146 "of "
147 "(out) neighbors.",
149 );
150
151 auto compute_required_recv_buf_size = [&]() {
152 return asserting_cast<size_t>(recv_count.get_single_element()) * this->in_degree();
153 };
154 recv_buf.resize_if_requested(compute_required_recv_buf_size);
155 KASSERT(
156 // if the recv type is user provided, kamping cannot make any assumptions about the required size of the
157 // recv buffer
159 "Recv buffer is not large enough to hold all received elements.",
161 );
162
163 // These KASSERTs are required to avoid a false warning from g++ in release mode
164 KASSERT(send_buf.data() != nullptr, assert::light);
165 KASSERT(recv_buf.data() != nullptr, assert::light);
166
168 send_buf.data(), // send_buf
169 send_count.get_single_element(), // send_count
170 send_type.get_single_element(), // send_type
171 recv_buf.data(), // recv_buf
172 recv_count.get_single_element(), // recv_count
173 recv_type.get_single_element(), // recv_type
174 this->mpi_communicator() // comm
175 );
176
177 this->mpi_error_hook(err, "MPI_Alltoall");
178 return make_mpi_result<std::tuple<Args...>>(
179 std::move(recv_buf), // recv_buf
180 std::move(send_count), // send_count
181 std::move(recv_count), // recv_count
182 std::move(send_type), // send_type
183 std::move(recv_type) // recv_type
184 );
185}
186/// @}
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
constexpr int light
Assertion level for lightweight assertions.
Definition assertion_levels.hpp:13
auto neighbor_alltoall(Args... args) const
Wrapper for MPI_Neighbor_alltoall.
Definition alltoall.hpp:87
static constexpr auto alloc_new
Convenience wrapper for creating library allocated containers. See AllocNewT for details.
Definition data_buffer.hpp:194
auto send_count(int count)
Passes count as send count to the underlying call.
Definition named_parameters.hpp:321
auto send_type(MPI_Datatype send_type)
Passes send_type as send type to the underlying call.
Definition named_parameters.hpp:1195
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_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:347
auto recv_count(int count)
Passes count as recv count to the underlying call.
Definition named_parameters.hpp:490
auto recv_type(MPI_Datatype recv_type)
Passes recv_type as recv type to the underlying call.
Definition named_parameters.hpp:1238
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:516
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.
Factory methods for buffer wrappers.
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.