KaMPIng 0.2.1
(Near) zero-overhead MPI wrapper for C++
Loading...
Searching...
No Matches
reduce_ops.hpp
Go to the documentation of this file.
1// This file is part of KaMPIng.
2//
3// Copyright 2021-2026 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 functor vocabulary, type traits, and RAII handle.
16///
17/// Provides:
18/// - `kamping::ops::` — functor types and commutativity tags
19/// - `kamping::types::mpi_operation_traits<Op, T>` — maps (functor, element type) → `MPI_Op`
20/// - `kamping::types::ScopedOp` — RAII wrapper for an `MPI_Op`
21/// - `kamping::types::ScopedFunctorOp` — creates an `MPI_Op` from a default-constructible C++ functor
22/// - `kamping::types::ScopedCallbackOp` — creates an `MPI_Op` from a raw MPI callback function pointer
23/// - `kamping::types::with_operation_functor` — maps a runtime `MPI_Op` to its functor
24
25#pragma once
26
27#include <algorithm>
28#include <functional>
29#include <limits>
30#include <new>
31#include <type_traits>
32#include <utility>
33
34#include <mpi.h>
35
36#include "kamping/kassert/kassert.hpp"
38
39// ---------------------------------------------------------------------------
40// kamping::ops — functor vocabulary
41// ---------------------------------------------------------------------------
42
43namespace kamping::ops::internal {
44
45/// @brief Wrapper struct for std::max.
46///
47/// `std::max` is a function, not a function object. This wrapper allows template matching for
48/// builtin MPI operation detection. The `<void>` specialization uses type deduction.
49/// @tparam T the type of the operands
50template <typename T>
51struct max_impl {
52 /// @brief Returns the maximum of the two parameters.
53 /// @param lhs the first operand
54 /// @param rhs the second operand
55 constexpr T operator()(T const& lhs, T const& rhs) const {
56 return std::max(lhs, rhs);
57 }
58};
59/// @brief Template specialization of max_impl without type parameter, leaving the operand type to be deduced.
60template <>
61struct max_impl<void> {
62 /// @brief Returns the maximum of the two parameters.
63 /// @tparam T the type of the operands
64 /// @param lhs the first operand
65 /// @param rhs the second operand
66 template <typename T>
67 constexpr auto operator()(T const& lhs, T const& rhs) const {
68 return std::max(lhs, rhs);
69 }
70};
71
72/// @brief Wrapper struct for std::min (same rationale as max_impl).
73/// @tparam T the type of the operands
74template <typename T>
75struct min_impl {
76 /// @brief Returns the minimum of the two parameters.
77 /// @param lhs the first operand
78 /// @param rhs the second operand
79 constexpr T operator()(T const& lhs, T const& rhs) const {
80 return std::min(lhs, rhs);
81 }
82};
83/// @brief Template specialization of min_impl without type parameter, leaving the operand type to be deduced.
84template <>
85struct min_impl<void> {
86 /// @brief Returns the minimum of the two parameters.
87 /// @tparam T the type of the operands
88 /// @param lhs the first operand
89 /// @param rhs the second operand
90 template <typename T>
91 constexpr auto operator()(T const& lhs, T const& rhs) const {
92 return std::min(lhs, rhs);
93 }
94};
95
96/// @brief Logical XOR function object (no STL equivalent).
97/// @tparam T type of the operands
98template <typename T>
100 /// @brief Returns the logical XOR of the two parameters.
101 /// @param lhs the first operand
102 /// @param rhs the second operand
103 constexpr bool operator()(T const& lhs, T const& rhs) const {
104 return (lhs && !rhs) || (!lhs && rhs);
105 }
106};
107/// @brief Template specialization of logical_xor_impl without type parameter, leaving operand types to be deduced.
108template <>
110 /// @brief Returns the logical XOR of the two parameters.
111 /// @tparam T type of the left operand
112 /// @tparam S type of the right operand
113 /// @param lhs the left operand
114 /// @param rhs the right operand
115 template <typename T, typename S>
116 constexpr bool operator()(T const& lhs, S const& rhs) const {
117 return (lhs && !rhs) || (!lhs && rhs);
118 }
119};
120
121/// @brief Tag for a commutative user-defined reduce operation.
123/// @brief Tag for a non-commutative user-defined reduce operation.
125/// @brief Tag for a reduce operation without a manually declared commutativity (builtin ops only).
127
128} // namespace kamping::ops::internal
129
130namespace kamping::ops {
131
132/// @brief Builtin maximum operation (`MPI_MAX`).
133template <typename T = void>
135
136/// @brief Builtin minimum operation (`MPI_MIN`).
137template <typename T = void>
139
140/// @brief Builtin summation (`MPI_SUM`).
141template <typename T = void>
142using plus = std::plus<T>;
143
144/// @brief Builtin multiplication (`MPI_PROD`).
145template <typename T = void>
146using multiplies = std::multiplies<T>;
147
148/// @brief Builtin logical AND (`MPI_LAND`).
149template <typename T = void>
150using logical_and = std::logical_and<T>;
151
152/// @brief Builtin bitwise AND (`MPI_BAND`).
153template <typename T = void>
154using bit_and = std::bit_and<T>;
155
156/// @brief Builtin logical OR (`MPI_LOR`).
157template <typename T = void>
158using logical_or = std::logical_or<T>;
159
160/// @brief Builtin bitwise OR (`MPI_BOR`).
161template <typename T = void>
162using bit_or = std::bit_or<T>;
163
164/// @brief Builtin logical XOR (`MPI_LXOR`).
165template <typename T = void>
167
168/// @brief Builtin bitwise XOR (`MPI_BXOR`).
169template <typename T = void>
170using bit_xor = std::bit_xor<T>;
171
172/// @brief Null operation (`MPI_OP_NULL`).
173template <typename T = void>
174struct null {};
175
176[[maybe_unused]] constexpr internal::commutative_tag commutative{}; ///< Tag: operation is commutative.
177[[maybe_unused]] constexpr internal::non_commutative_tag non_commutative{}; ///< Tag: operation is non-commutative.
178
179} // namespace kamping::ops
180
181// ---------------------------------------------------------------------------
182// kamping::types — mpi_operation_traits, ScopedOp, with_operation_functor
183// ---------------------------------------------------------------------------
184
185namespace kamping::types {
186
187#ifdef KAMPING_DOXYGEN_ONLY
188/// @brief Type trait that maps a (functor type, element type) pair to its builtin `MPI_Op`.
189///
190/// `mpi_operation_traits<Op, T>::is_builtin` is `true` when `Op` applied to `T` corresponds to
191/// a predefined MPI operation constant. When `true`, `::op()` returns that constant and
192/// `::identity` holds the identity element for the operation.
193///
194/// Example:
195/// @code
196/// mpi_operation_traits<kamping::ops::plus<>, int>::is_builtin // true
197/// mpi_operation_traits<kamping::ops::plus<>, int>::op() // MPI_SUM
198/// mpi_operation_traits<std::plus<>, int>::is_builtin // true
199/// mpi_operation_traits<std::minus<>, int>::is_builtin // false
200/// @endcode
201/// @tparam Op Functor type of the operation.
202/// @tparam T Element type to apply the operation to.
203template <typename Op, typename T>
205 /// @brief \c true if \c Op applied to \c T corresponds to a predefined MPI operation constant.
206 static constexpr bool is_builtin;
207
208 /// @brief The identity element for this operation and data type.
209 ///
210 /// Only defined when \c is_builtin is \c true.
211 static constexpr T identity;
212
213 /// @brief Returns the predefined \c MPI_Op constant for this operation.
214 ///
215 /// Only defined when \c is_builtin is \c true.
216 static MPI_Op op();
217};
218#else
219
220template <typename Op, typename T, typename Enable = void>
222 static constexpr bool is_builtin = false;
223};
224
225template <typename T, typename S>
226struct mpi_operation_traits<
227 kamping::ops::max<S>,
228 T,
229 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
230 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
231 )> > {
232 static constexpr bool is_builtin = true;
233 static constexpr T identity = std::numeric_limits<T>::lowest();
234 static MPI_Op op() {
235 return MPI_MAX;
236 }
237};
238
239template <typename T, typename S>
240struct mpi_operation_traits<
241 kamping::ops::min<S>,
242 T,
243 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
244 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
245 )> > {
246 static constexpr bool is_builtin = true;
247 static constexpr T identity = std::numeric_limits<T>::max();
248 static MPI_Op op() {
249 return MPI_MIN;
250 }
251};
252
253template <typename T, typename S>
254struct mpi_operation_traits<
255 kamping::ops::plus<S>,
256 T,
257 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
258 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
259 || builtin_type<T>::category == TypeCategory::complex
260 )> > {
261 static constexpr bool is_builtin = true;
262 static constexpr T identity = 0;
263 static MPI_Op op() {
264 return MPI_SUM;
265 }
266};
267
268template <typename T, typename S>
269struct mpi_operation_traits<
270 kamping::ops::multiplies<S>,
271 T,
272 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
273 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
274 || builtin_type<T>::category == TypeCategory::complex
275 )> > {
276 static constexpr bool is_builtin = true;
277 static constexpr T identity = 1;
278 static MPI_Op op() {
279 return MPI_PROD;
280 }
281};
282
283template <typename T, typename S>
284struct mpi_operation_traits<
285 kamping::ops::logical_and<S>,
286 T,
287 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
288 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
289 )> > {
290 static constexpr bool is_builtin = true;
291 static constexpr T identity = true;
292 static MPI_Op op() {
293 return MPI_LAND;
294 }
295};
296
297template <typename T, typename S>
298struct mpi_operation_traits<
299 kamping::ops::logical_or<S>,
300 T,
301 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
302 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
303 )> > {
304 static constexpr bool is_builtin = true;
305 static constexpr T identity = false;
306 static MPI_Op op() {
307 return MPI_LOR;
308 }
309};
310
311template <typename T, typename S>
312struct mpi_operation_traits<
313 kamping::ops::logical_xor<S>,
314 T,
315 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
316 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
317 )> > {
318 static constexpr bool is_builtin = true;
319 static constexpr T identity = false;
320 static MPI_Op op() {
321 return MPI_LXOR;
322 }
323};
324
325template <typename T, typename S>
326struct mpi_operation_traits<
327 kamping::ops::bit_and<S>,
328 T,
329 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
330 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
331 )> > {
332 static constexpr bool is_builtin = true;
333 static constexpr T identity = ~(T{0});
334 static MPI_Op op() {
335 return MPI_BAND;
336 }
337};
338
339template <typename T, typename S>
340struct mpi_operation_traits<
341 kamping::ops::bit_or<S>,
342 T,
343 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
344 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
345 )> > {
346 static constexpr bool is_builtin = true;
347 static constexpr T identity = T{0};
348 static MPI_Op op() {
349 return MPI_BOR;
350 }
351};
352
353template <typename T, typename S>
354struct mpi_operation_traits<
355 kamping::ops::bit_xor<S>,
356 T,
357 std::enable_if_t<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
358 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
359 )> > {
360 static constexpr bool is_builtin = true;
361 static constexpr T identity = T{0};
362 static MPI_Op op() {
363 return MPI_BXOR;
364 }
365};
366
367#endif // KAMPING_DOXYGEN_ONLY
368
369// ---------------------------------------------------------------------------
370// ScopedOp — RAII handle for MPI_Op
371// ---------------------------------------------------------------------------
372
373/// @brief RAII wrapper for an `MPI_Op`.
374///
375/// Calls `MPI_Op_free` on destruction only when `owns` is true (i.e. the op was created via
376/// `MPI_Op_create` for a user-defined functor). Predefined MPI constants (`MPI_SUM`,
377/// `MPI_MAX`, …) are never freed.
378///
379/// Analogous to `ScopedDatatype` for `MPI_Datatype`.
380class ScopedOp {
381public:
382 /// @brief Constructs an empty, non-owning handle (`MPI_OP_NULL`).
383 ScopedOp() noexcept : _op(MPI_OP_NULL), _owns(false) {}
384
385 /// @brief Wrap an existing `MPI_Op`.
386 /// @param op The op to wrap.
387 /// @param owns If `true`, `MPI_Op_free` is called on destruction.
388 ScopedOp(MPI_Op op, bool owns) noexcept : _op(op), _owns(owns) {}
389
390 ScopedOp(ScopedOp const&) = delete;
391 ScopedOp& operator=(ScopedOp const&) = delete;
392
393 /// @brief Move constructor. Transfers ownership; the moved-from handle no longer frees the op.
394 ScopedOp(ScopedOp&& other) noexcept : _op(other._op), _owns(other._owns) {
395 other._owns = false;
396 }
397 /// @brief Move assignment. Frees any currently owned op, then transfers ownership.
399 if (this != &other) {
400 _free();
401 _op = other._op;
402 _owns = other._owns;
403 other._owns = false;
404 }
405 return *this;
406 }
407
408 ~ScopedOp() {
409 _free();
410 }
411
412 /// @returns The underlying `MPI_Op`.
414 return _op;
415 }
416
417private:
418 void _free() noexcept {
419 if (_owns) {
420 int const err = MPI_Op_free(&_op);
421 KAMPING_ASSERT(err == MPI_SUCCESS, "MPI_Op_free failed");
422 _owns = false;
423 }
424 }
425
426 MPI_Op _op;
427 bool _owns;
428};
429
430// ---------------------------------------------------------------------------
431// ScopedFunctorOp — MPI_Op_create from a default-constructible C++ functor
432// ---------------------------------------------------------------------------
433
434/// @brief RAII handle that creates an `MPI_Op` from a default-constructible C++ functor.
435///
436/// Calls `MPI_Op_create` on construction and `MPI_Op_free` on destruction.
437/// The functor is invoked via `MPI_Op_create`'s callback and must be default-constructible
438/// (i.e. stateless or state carried via static variables). For capturing lambdas use `ScopedCallbackOp`.
439///
440/// @tparam is_commutative Whether the operation is commutative.
441/// @tparam T Element type the functor operates on. Must be destructible and
442/// move- or copy-constructible. Notably T need *not* be assignable --
443/// see _execute()'s comment.
444/// @tparam Op Functor type. Must be default-constructible and callable as `T(T const&, T const&)`.
445template <bool is_commutative, typename T, typename Op>
447 static_assert(
448 std::is_default_constructible_v<Op>,
449 "ScopedFunctorOp requires a default-constructible functor. Use ScopedCallbackOp for lambdas."
450 );
451 static_assert(std::is_invocable_r_v<T, Op, T const&, T const&>, "Op must be callable as T(T const&, T const&).");
452 static_assert(
453 std::is_destructible_v<T> && (std::is_move_constructible_v<T> || std::is_copy_constructible_v<T>),
454 "T must be destructible and move- or copy-constructible (T need not be assignable -- "
455 "_execute() combines by destroying and reconstructing elements in place, not by assignment)."
456 );
457
458public:
459 /// @brief Creates an `MPI_Op` for the given functor.
460 ScopedFunctorOp(Op op) : _functor(std::move(op)), _op(_make_scoped_op()) {}
461
462 ScopedFunctorOp(ScopedFunctorOp const&) = delete;
463 ScopedFunctorOp& operator=(ScopedFunctorOp const&) = delete;
465 ScopedFunctorOp& operator=(ScopedFunctorOp&&) = delete;
466
467 /// @returns The underlying `MPI_Op`. Do not free manually — the destructor does it.
469 return _op.get();
470 }
471
472 /// @brief Applies the functor to two values.
473 T operator()(T const& lhs, T const& rhs) const {
474 return _functor(lhs, rhs);
475 }
476
477private:
478 /// @brief MPI callback: applies a default-constructed `Op` element-wise.
479 ///
480 /// Combines by copy-constructing the winner into a local first (`Op` returns `T` by value),
481 /// then destroying `inout[i]` and reconstructing it in place via an explicit destructor
482 /// call and placement-new -- not by assignment. (kamping-types targets C++17, so this uses
483 /// the underlying C++17-legal mechanism directly rather than C++20's
484 /// std::destroy_at/std::construct_at, which are thin wrappers around the same thing.) This
485 /// means T only needs to be destructible and move-/copy-constructible, not assignable: e.g.
486 /// std::pair<const K, V> (a std::map's/flat_hash_map's value type) has a deleted
487 /// `operator=` but a perfectly usable copy constructor, and works here.
488 ///
489 /// Deliberately *not* gated on std::is_trivially_copyable_v<T>: that trait is a poor proxy
490 /// for what's needed. Even an ordinary std::pair<int, int> is never trivially copyable,
491 /// because the standard never specifies pair's assignment operators as defaulted/trivial
492 /// (see https://stackoverflow.com/q/58283694), even though copying two ints plainly is
493 /// trivial -- requiring it would reject the common case. The actual precondition -- that
494 /// invec[i]/inout[i] may be read as a live T, i.e. T's MPI-transported byte representation
495 /// is meaningful without going through a constructor -- already had to hold for any T
496 /// reaching this callback under the old assignment-based implementation too (it also read
497 /// `*in`/`*inout` as `T const&`); this function adds no new requirement on top of that.
498 static void _execute(void* invec, void* inoutvec, int* len, MPI_Datatype* /*datatype*/) {
499 T* in = static_cast<T*>(invec);
500 T* inout = static_cast<T*>(inoutvec);
501 for (int i = 0; i < *len; ++i) {
502 T combined = Op{}(in[i], inout[i]);
503 inout[i].~T();
504 ::new (static_cast<void*>(inout + i)) T(std::move(combined));
505 }
506 }
507
508 static ScopedOp _make_scoped_op() {
509 MPI_Op raw;
510 MPI_Op_create(_execute, static_cast<int>(is_commutative), &raw);
511 return ScopedOp{raw, true};
512 }
513
514 Op _functor;
515 ScopedOp _op;
516};
517
518// ---------------------------------------------------------------------------
519// ScopedCallbackOp — MPI_Op_create from a raw MPI callback function pointer
520// ---------------------------------------------------------------------------
521
522/// @brief RAII handle that creates an `MPI_Op` from a raw MPI callback function pointer.
523///
524/// Calls `MPI_Op_create` on construction and `MPI_Op_free` on destruction.
525/// A default-constructed `ScopedCallbackOp` is empty (`MPI_OP_NULL`, non-owning).
526/// Supports move construction and assignment; the moved-from handle becomes empty.
527///
528/// Typically used for lambdas with captures, where the lambda is stored separately and a
529/// raw function pointer (via a static trampoline) is passed to `MPI_Op_create`.
530///
531/// @tparam is_commutative Whether the operation is commutative.
532template <bool is_commutative>
534public:
535 /// @brief The MPI callback signature expected by `MPI_Op_create`.
536 using callback_type = void (*)(void*, void*, int*, MPI_Datatype*);
537
538 /// @brief Constructs an empty, non-owning handle (`MPI_OP_NULL`).
540
541 /// @brief Creates an `MPI_Op` for the given callback.
542 /// @param ptr Non-null MPI callback function pointer.
543 explicit ScopedCallbackOp(callback_type ptr) : _op(_make_scoped_op(ptr)) {
544 KAMPING_ASSERT(ptr != nullptr);
545 }
546
547 ScopedCallbackOp(ScopedCallbackOp const&) = delete;
548 ScopedCallbackOp& operator=(ScopedCallbackOp const&) = delete;
549
550 /// @brief Move constructor. The moved-from handle becomes empty.
552 /// @brief Move assignment. Frees any currently owned op, then takes ownership.
554
555 /// @returns The underlying `MPI_Op` (`MPI_OP_NULL` if default-constructed). Do not free manually.
557 return _op.get();
558 }
559
560private:
561 static ScopedOp _make_scoped_op(callback_type ptr) {
562 MPI_Op raw;
563 MPI_Op_create(ptr, static_cast<int>(is_commutative), &raw);
564 return ScopedOp{raw, true};
565 }
566
567 ScopedOp _op; // default-constructed: MPI_OP_NULL, non-owning
568};
569
570// ---------------------------------------------------------------------------
571// with_operation_functor — runtime MPI_Op → functor dispatch
572// ---------------------------------------------------------------------------
573
574/// @brief Calls `func` with the functor object corresponding to the given builtin `MPI_Op`.
575///
576/// For unknown ops, calls `func(kamping::ops::null<>{})`. Useful for implementing
577/// `MPI_Reduce_local`-style helpers that need a C++ callable for a runtime `MPI_Op`.
578///
579/// @tparam Functor Callable accepting any `kamping::ops::*` functor type.
580template <typename Functor>
581auto with_operation_functor(MPI_Op op, Functor&& func) {
582 if (op == MPI_MAX)
583 return func(ops::max<>{});
584 else if (op == MPI_MIN)
585 return func(ops::min<>{});
586 else if (op == MPI_SUM)
587 return func(ops::plus<>{});
588 else if (op == MPI_PROD)
589 return func(ops::multiplies<>{});
590 else if (op == MPI_LAND)
591 return func(ops::logical_and<>{});
592 else if (op == MPI_LOR)
593 return func(ops::logical_or<>{});
594 else if (op == MPI_LXOR)
595 return func(ops::logical_xor<>{});
596 else if (op == MPI_BAND)
597 return func(ops::bit_and<>{});
598 else if (op == MPI_BOR)
599 return func(ops::bit_or<>{});
600 else if (op == MPI_BXOR)
601 return func(ops::bit_xor<>{});
602 else
603 return func(ops::null<>{});
604}
605
606} // namespace kamping::types
STL-compatible allocator for requesting memory using the builtin MPI allocator.
Definition allocator.hpp:32
RAII handle that creates an MPI_Op from a raw MPI callback function pointer.
Definition reduce_ops.hpp:533
MPI_Op get() const noexcept
Definition reduce_ops.hpp:556
void(*)(void *, void *, int *, MPI_Datatype *) callback_type
The MPI callback signature expected by MPI_Op_create.
Definition reduce_ops.hpp:536
ScopedCallbackOp(ScopedCallbackOp &&) noexcept=default
Move constructor. The moved-from handle becomes empty.
ScopedCallbackOp() noexcept=default
Constructs an empty, non-owning handle (MPI_OP_NULL).
RAII handle that creates an MPI_Op from a default-constructible C++ functor.
Definition reduce_ops.hpp:446
T operator()(T const &lhs, T const &rhs) const
Applies the functor to two values.
Definition reduce_ops.hpp:473
MPI_Op get() const noexcept
Definition reduce_ops.hpp:468
ScopedFunctorOp(Op op)
Creates an MPI_Op for the given functor.
Definition reduce_ops.hpp:460
RAII wrapper for an MPI_Op.
Definition reduce_ops.hpp:380
ScopedOp & operator=(ScopedOp &&other) noexcept
Move assignment. Frees any currently owned op, then transfers ownership.
Definition reduce_ops.hpp:398
MPI_Op get() const noexcept
Definition reduce_ops.hpp:413
ScopedOp(MPI_Op op, bool owns) noexcept
Wrap an existing MPI_Op.
Definition reduce_ops.hpp:388
ScopedOp() noexcept
Constructs an empty, non-owning handle (MPI_OP_NULL).
Definition reduce_ops.hpp:383
ScopedOp(ScopedOp &&other) noexcept
Move constructor. Transfers ownership; the moved-from handle no longer frees the op.
Definition reduce_ops.hpp:394
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:1219
Mapping of C++ datatypes to builtin MPI types.
STL namespace.
std::logical_and< T > logical_and
Builtin logical AND (MPI_LAND).
Definition reduce_ops.hpp:150
constexpr internal::commutative_tag commutative
Tag: operation is commutative.
Definition reduce_ops.hpp:176
std::bit_or< T > bit_or
Builtin bitwise OR (MPI_BOR).
Definition reduce_ops.hpp:162
std::logical_or< T > logical_or
Builtin logical OR (MPI_LOR).
Definition reduce_ops.hpp:158
std::bit_and< T > bit_and
Builtin bitwise AND (MPI_BAND).
Definition reduce_ops.hpp:154
std::bit_xor< T > bit_xor
Builtin bitwise XOR (MPI_BXOR).
Definition reduce_ops.hpp:170
std::plus< T > plus
Builtin summation (MPI_SUM).
Definition reduce_ops.hpp:142
constexpr internal::non_commutative_tag non_commutative
Tag: operation is non-commutative.
Definition reduce_ops.hpp:177
std::multiplies< T > multiplies
Builtin multiplication (MPI_PROD).
Definition reduce_ops.hpp:146
Tag for a commutative user-defined reduce operation.
Definition reduce_ops.hpp:122
constexpr bool operator()(T const &lhs, S const &rhs) const
Returns the logical XOR of the two parameters.
Definition reduce_ops.hpp:116
Logical XOR function object (no STL equivalent).
Definition reduce_ops.hpp:99
constexpr bool operator()(T const &lhs, T const &rhs) const
Returns the logical XOR of the two parameters.
Definition reduce_ops.hpp:103
constexpr auto operator()(T const &lhs, T const &rhs) const
Returns the maximum of the two parameters.
Definition reduce_ops.hpp:67
Wrapper struct for std::max.
Definition reduce_ops.hpp:51
constexpr T operator()(T const &lhs, T const &rhs) const
Returns the maximum of the two parameters.
Definition reduce_ops.hpp:55
constexpr auto operator()(T const &lhs, T const &rhs) const
Returns the minimum of the two parameters.
Definition reduce_ops.hpp:91
Wrapper struct for std::min (same rationale as max_impl).
Definition reduce_ops.hpp:75
constexpr T operator()(T const &lhs, T const &rhs) const
Returns the minimum of the two parameters.
Definition reduce_ops.hpp:79
Tag for a non-commutative user-defined reduce operation.
Definition reduce_ops.hpp:124
Tag for a reduce operation without a manually declared commutativity (builtin ops only).
Definition reduce_ops.hpp:126
Null operation (MPI_OP_NULL).
Definition reduce_ops.hpp:174
Type trait that maps a (functor type, element type) pair to its builtin MPI_Op.
Definition reduce_ops.hpp:204
static constexpr bool is_builtin
true if Op applied to T corresponds to a predefined MPI operation constant.
Definition reduce_ops.hpp:206
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:211