KaMPIng 0.2.1
(Near) zero-overhead MPI wrapper for C++
Loading...
Searching...
No Matches
mpi_ops.hpp
Go to the documentation of this file.
1// This file is part of KaMPIng.
2//
3// Copyright 2021-2022 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/// @file
15/// @brief MPI reduction operation wrappers.
16///
17/// The functor vocabulary (`kamping::ops::`), type traits (`mpi_operation_traits`),
18/// `ScopedOp`, `ScopedFunctorOp`, and `ScopedCallbackOp` live in
19/// `kamping/types/reduce_ops.hpp` (the `kamping-types` module) and are included from there.
20/// This file adds `ReduceOperation`, which selects among these building blocks based on
21/// the operation and commutative tag types.
22
23#pragma once
24
25#include <algorithm>
26#include <type_traits>
27
28#include <mpi.h>
29
30#include "kamping/kassert/kassert.hpp"
33
34namespace kamping {
35namespace internal {
36
37// Bring kamping::types::mpi_operation_traits into kamping::internal so that existing code
38// that references kamping::internal::mpi_operation_traits<Op, T> continues to compile without
39// change. Specializations are resolved through the primary template in kamping::types.
41
42// Bring with_operation_functor into kamping::internal (backward compat).
44
45// ---------------------------------------------------------------------------
46// ReduceOperation — high-level op wrapper used by collectives
47// ---------------------------------------------------------------------------
48
49#ifdef KAMPING_DOXYGEN_ONLY
50
51/// @brief Wraps an operation and translates it to a builtin \c MPI_Op or constructs a custom operation.
52/// @tparam T the argument type of the operation
53/// @tparam Op the type of the operation
54/// @tparam Commutative tag indicating if this type is commutative
55template <typename T, typename Op, typename Commutative>
57public:
58 /// @brief Constructs an operation wrapper.
59 /// @param op the operation (function object, lambda, or \c std::function)
60 /// @param commutative commutativity tag (\c kamping::ops::commutative or \c kamping::ops::non_commutative)
62
63 static constexpr bool is_builtin; ///< True if this is a predefined MPI operation.
64 static constexpr bool commutative; ///< True if the operation is commutative.
65
66 /// @returns the \c MPI_Op associated with this operation.
68
69 /// @brief Call the underlying operation with the provided arguments.
70 T operator()(T const& lhs, T const& rhs) const;
71
72 /// @brief Returns the identity element for this operation and data type.
73 ///
74 /// Only available when `is_builtin == true`. For custom operations this member does not exist;
75 /// callers must guard with `if constexpr (operation.is_builtin)`.
77};
78
79#else
80
81// Primary: custom default-constructible functor.
82template <typename T, typename Op, typename Commutative, class Enable = void>
83class ReduceOperation {
84 static_assert(
85 std::is_same_v<
87 kamping::ops::internal::
88 commutative_tag> || std::is_same_v<Commutative, kamping::ops::internal::non_commutative_tag>,
89 "For custom operations you have to specify whether they are commutative."
90 );
91
92public:
94 static constexpr bool is_builtin = false;
95 static constexpr bool commutative = std::is_same_v<Commutative, kamping::ops::internal::commutative_tag>;
96
97 T operator()(T const& lhs, T const& rhs) const {
98 return _operation(lhs, rhs);
99 }
100
101 MPI_Op op() {
102 return _operation.get();
103 }
104
105private:
107};
108
109// Specialization: raw MPI_Op passthrough.
110template <typename T>
111class ReduceOperation<T, MPI_Op, ops::internal::undefined_commutative_tag, void> {
112public:
113 ReduceOperation(MPI_Op op, ops::internal::undefined_commutative_tag = {}) : _op(op) {}
114 static constexpr bool is_builtin = false;
115
116 T operator()(T const& lhs, T const& rhs) const {
117 KAMPING_ASSERT(_op != MPI_OP_NULL, "Cannot call MPI_OP_NULL.");
118 T result;
119 internal::with_operation_functor(_op, [&result, lhs, rhs, this](auto operation) {
120 if constexpr (!std::is_same_v<decltype(operation), ops::null<> >) {
121 result = operation(lhs, rhs);
122 } else {
123 result = rhs;
124 MPI_Reduce_local(&lhs, &result, 1, mpi_datatype<T>(), _op);
125 }
126 });
127 return result;
128 }
129
130 MPI_Op op() {
131 return _op;
132 }
133
134private:
135 MPI_Op _op;
136};
137
138// Specialization: builtin op — maps directly to a predefined MPI_Op constant.
139template <typename T, typename Op, typename Commutative>
140class ReduceOperation<T, Op, Commutative, std::enable_if_t<mpi_operation_traits<Op, T>::is_builtin> > {
141 static_assert(
142 std::is_same_v<Commutative, kamping::ops::internal::undefined_commutative_tag>,
143 "For builtin operations you don't need to specify whether they are commutative."
144 );
145
146public:
147 ReduceOperation(Op&&, Commutative) {}
148 static constexpr bool is_builtin = true;
149 static constexpr bool commutative = true;
150
151 MPI_Op op() {
153 }
154
155 T operator()(T const& lhs, T const& rhs) const {
156 return Op{}(lhs, rhs);
157 }
158
159 T identity() {
161 }
162};
163
164// Specialization: non-default-constructible functor (lambda with captures).
165template <typename T, typename Op, typename Commutative>
166class ReduceOperation<T, Op, Commutative, std::enable_if_t<!std::is_default_constructible_v<Op> > > {
167 static_assert(
168 std::is_same_v<
169 Commutative,
170 kamping::ops::internal::
171 commutative_tag> || std::is_same_v<Commutative, kamping::ops::internal::non_commutative_tag>,
172 "For custom operations you have to specify whether they are commutative."
173 );
174
175public:
176 ReduceOperation(Op&& op, Commutative) : _op(op) {
177 // Each lambda type is distinct, so a static Op per instantiation is safe for a single
178 // concurrent reduction.
179 static Op func = _op;
180
182 [](void* invec, void* inoutvec, int* len, MPI_Datatype* /*datatype*/) {
183 T* invec_ = static_cast<T*>(invec);
184 T* inoutvec_ = static_cast<T*>(inoutvec);
185 std::transform(invec_, invec_ + *len, inoutvec_, inoutvec_, func);
186 };
188 }
189 static constexpr bool is_builtin = false;
190 static constexpr bool commutative = std::is_same_v<Commutative, kamping::ops::internal::commutative_tag>;
191
192 MPI_Op op() {
193 return _operation.get();
194 }
195
196 T operator()(T const& lhs, T const& rhs) const {
197 return _op(lhs, rhs);
198 }
199
200private:
201 Op _op;
203};
204
205#endif // KAMPING_DOXYGEN_ONLY
206
207} // namespace internal
208} // namespace kamping
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
Wraps an operation and translates it to a builtin MPI_Op or constructs a custom operation.
Definition mpi_ops.hpp:56
ReduceOperation(Op &&op, Commutative commutative)
Constructs an operation wrapper.
static constexpr bool commutative
True if the operation is commutative.
Definition mpi_ops.hpp:64
T operator()(T const &lhs, T const &rhs) const
Call the underlying operation with the provided arguments.
static constexpr bool is_builtin
True if this is a predefined MPI operation.
Definition mpi_ops.hpp:63
T identity()
Returns the identity element for this operation and data type.
RAII handle that creates an MPI_Op from a raw MPI callback function pointer.
Definition reduce_ops.hpp:501
RAII handle that creates an MPI_Op from a default-constructible C++ functor.
Definition reduce_ops.hpp:442
Utility that maps C++ types to types that can be understood by MPI.
STL namespace.
MPI reduction operation functor vocabulary, type traits, and RAII handle.
auto with_operation_functor(MPI_Op op, Functor &&func)
Calls func with the functor object corresponding to the given builtin MPI_Op.
Definition reduce_ops.hpp:549
Type trait that maps a (functor type, element type) pair to its builtin MPI_Op.
Definition reduce_ops.hpp:202
static MPI_Op op()
Returns the predefined MPI_Op constant for this operation.
static constexpr T identity
The identity element for this operation and data type.
Definition reduce_ops.hpp:209