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 <tuple>
18#include <type_traits>
19
20#include <kassert/kassert.hpp>
21#include <mpi.h>
22
23#include "kamping/assertion_levels.hpp"
25#include "kamping/collectives/collectives_helpers.hpp"
29#include "kamping/result.hpp"
30#include "kamping/topology_communicator.hpp"
31
32/// @addtogroup kamping_collectives
33/// @{
34
35/// @brief Wrapper for \c MPI_Neighbor_alltoall.
36///
37/// This wrapper for \c MPI_Neighbor_alltoall sends the same amount of data
38/// from a rank i to each of its neighbour j for which an edge (i,j) in the communication graph exists. The following
39/// buffers are required:
40/// @todo check again once the concrete semantics (potential differing number of send/recv counts) of
41/// MPI_Neighbor_alltoall has been clarified.
42/// - \ref kamping::send_buf() containing the data that is sent to each neighbor. This buffer has to be divisible by the
43/// out degree unless a send_count or a send_type is explicitly given as parameter.
44///
45/// The following parameters are optional:
46/// - \ref kamping::send_count() specifying how many elements are sent. If
47/// omitted, the size of send buffer divided by number of outgoing neighbors is used.
48/// This has to be the same on all ranks.
49/// This parameter is mandatory if \ref kamping::send_type() is given.
50///
51/// - \ref kamping::recv_count() specifying how many elements are received. If
52/// omitted, the value of send_counts will be used.
53/// This parameter is mandatory if \ref kamping::recv_type() is given.
54///
55/// - \ref kamping::recv_buf() specifying a buffer for the output. A buffer of at least
56/// `recv_count * in degree` is required.
57///
58/// - \ref kamping::send_type() specifying the \c MPI datatype to use as send type. If omitted, the \c MPI datatype is
59/// derived automatically based on send_buf's underlying \c value_type.
60///
61/// - \ref kamping::recv_type() specifying the \c MPI datatype to use as recv type. If omitted, the \c MPI datatype is
62/// derived automatically based on recv_buf's underlying \c value_type.
63///
64/// @tparam Args Automatically deduced template parameters.
65/// @param args All required and any number of the optional buffers described above.
66/// @return Result type wrapping the output parameters to be returned by value.
67///
68/// @see \ref docs/parameter_handling.md for general information about parameter handling in KaMPIng.
69/// <hr>
70/// \include{doc} docs/resize_policy.dox
71
72template <
73 template <typename...>
74 typename DefaultContainerType,
75 template <typename, template <typename...> typename>
76 typename... Plugins>
77template <typename... Args>
79 using namespace internal;
81 Args,
84 );
85 // Get the buffers
86 auto const send_buf =
87 internal::select_parameter_type<internal::ParameterType::send_buf>(args...).construct_buffer_or_rebind();
88 using send_value_type = typename std::remove_reference_t<decltype(send_buf)>::value_type;
89 using default_recv_value_type = std::remove_const_t<send_value_type>;
90
92 auto recv_buf =
93 internal::select_parameter_type_or_default<internal::ParameterType::recv_buf, default_recv_buf_type>(
94 std::tuple(),
95 args...
96 )
98 using recv_value_type = typename std::remove_reference_t<decltype(recv_buf)>::value_type;
99
100 static_assert(!std::is_const_v<recv_value_type>, "The receive buffer must not have a const value_type.");
101
102 auto [send_type, recv_type] =
103 internal::determine_mpi_datatypes<send_value_type, recv_value_type, decltype(recv_buf)>(args...);
104 [[maybe_unused]] constexpr bool recv_type_has_to_be_deduced = has_to_be_computed<decltype(recv_type)>;
105
106 // Get the send counts
108 auto send_count =
109 internal::select_parameter_type_or_default<internal::ParameterType::send_count, default_send_count_type>(
110 std::tuple(),
111 args...
112 )
113 .construct_buffer_or_rebind();
115 if constexpr (do_compute_send_count) {
116 send_count.underlying() =
117 this->out_degree() == 0 ? 0 : asserting_cast<int>(send_buf.size() / this->out_degree());
118 }
119 // Get the recv counts
121 auto recv_count =
122 internal::select_parameter_type_or_default<internal::ParameterType::recv_count, default_recv_count_type>(
123 std::tuple(),
124 args...
125 )
126 .construct_buffer_or_rebind();
127
129 if constexpr (do_compute_recv_count) {
130 recv_count.underlying() = send_count.get_single_element();
131 }
132
133 KASSERT(
134 // @todo check this condition once we know the exact intended semantics of neighbor_alltoall
135 (!do_compute_send_count || this->out_degree() == 0 || send_buf.size() % this->out_degree() == 0lu),
136 "There are no send counts given and the number of elements in send_buf is not divisible by the number "
137 "of "
138 "(out) neighbors.",
140 );
141
142 auto compute_required_recv_buf_size = [&]() {
143 return asserting_cast<size_t>(recv_count.get_single_element()) * this->in_degree();
144 };
145 recv_buf.resize_if_requested(compute_required_recv_buf_size);
146 KASSERT(
147 // if the recv type is user provided, kamping cannot make any assumptions about the required size of the
148 // recv buffer
150 "Recv buffer is not large enough to hold all received elements.",
152 );
153
154 // These KASSERTs are required to avoid a false warning from g++ in release mode
155 KASSERT(send_buf.data() != nullptr, assert::light);
156 KASSERT(recv_buf.data() != nullptr, assert::light);
157
159 send_buf.data(), // send_buf
160 send_count.get_single_element(), // send_count
161 send_type.get_single_element(), // send_type
162 recv_buf.data(), // recv_buf
163 recv_count.get_single_element(), // recv_count
164 recv_type.get_single_element(), // recv_type
165 this->mpi_communicator() // comm
166 );
167
168 this->mpi_error_hook(err, "MPI_Alltoall");
169 return make_mpi_result<std::tuple<Args...>>(
170 std::move(recv_buf), // recv_buf
171 std::move(send_count), // send_count
172 std::move(recv_count), // recv_count
173 std::move(send_type), // send_type
174 std::move(recv_type) // recv_type
175 );
176}
177/// @}
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:78
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:323
auto recv_count(int count)
Passes count as recv count to the underlying call.
Definition named_parameters.hpp:492
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 send_buf(internal::ignore_t< Data > ignore)
Generates a dummy send buf that wraps a nullptr.
Definition named_parameters.hpp:53
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.