KaMPIng 0.1.1
Flexible and (near) zero-overhead C++ bindings for MPI
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 Definitions for builtin MPI operations
16
17#pragma once
18
19#include <algorithm>
20#include <functional>
21#include <type_traits>
22
23#include <mpi.h>
24
26
27namespace kamping {
28namespace internal {
29
30/// @brief Wrapper struct for std::max
31///
32/// Other than the operators defined in `<functional>` like \c std::plus, \c std::max is a function and not a function
33/// object. To enable template matching for detection of builtin MPI operations we therefore need to wrap it.
34/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
35///
36/// @tparam T the type of the operands
37template <typename T>
38struct max_impl {
39 /// @brief Returns the maximum of the two parameters
40 /// @param lhs the first operand
41 /// @param rhs the second operand
42 /// @return the maximum
43 constexpr T operator()(T const& lhs, T const& rhs) const {
44 // return std::max<const T&>(lhs, rhs);
45 return std::max(lhs, rhs);
46 }
47};
48
49/// @brief Template specialization for \c kamping::internal::max_impl without type parameter, which leaves the operand
50/// type to be deduced.
51///
52/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
53template <>
54struct max_impl<void> {
55 /// @brief Returns the maximum of the two parameters
56 /// @param lhs the first operand
57 /// @param rhs the second operand
58 /// @tparam T the type of the operands
59 /// @return the maximum
60 template <typename T>
61 constexpr auto operator()(T const& lhs, T const& rhs) const {
62 return std::max(lhs, rhs);
63 }
64};
65
66/// @brief Wrapper struct for std::min
67///
68/// Other than the operators defined in `<functional>` like \c std::plus, \c std::min is a function and not a function
69/// object. To enable template matching for detection of builtin MPI operations we therefore need to wrap it.
70/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
71///
72/// @tparam T the type of the operands
73template <typename T>
74struct min_impl {
75 /// @brief returns the maximum of the two parameters
76 /// @param lhs the first operand
77 /// @param rhs the second operand
78 /// @return the maximum
79 constexpr T operator()(T const& lhs, T const& rhs) const {
80 return std::min(lhs, rhs);
81 }
82};
83
84/// @brief Template specialization for \c kamping::internal::min_impl without type parameter, which leaves the operand
85/// type to be deduced.
86/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
87template <>
88struct min_impl<void> {
89 /// @brief Returns the maximum of the two parameters
90 /// @param lhs the first operand
91 /// @param rhs the second operand
92 /// @tparam T the type of the operands
93 /// @return the maximum
94 template <typename T>
95 constexpr auto operator()(T const& lhs, T const& rhs) const {
96 return std::min(lhs, rhs);
97 }
98};
99
100/// @brief Wrapper struct for logical xor, as the standard library does not provided a function object for it.
101///
102/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
103///
104/// @tparam T type of the operands
105template <typename T>
107 /// @brief Returns the logical xor of the two parameters
108 /// @param lhs the first operand
109 /// @param rhs the second operand
110 /// @return the logical xor
111 constexpr bool operator()(T const& lhs, T const& rhs) const {
112 return (lhs && !rhs) || (!lhs && rhs);
113 }
114};
115
116/// @brief Template specialization for \c kamping::internal::logical_xor_impl without type parameter, which leaves to
117/// operand type to be deduced.
118/// The actual implementation is used in case that the operation is a builtin operation for the given datatype.
119template <>
121 /// @brief Returns the logical xor of the two parameters
122 /// @param lhs the left operand
123 /// @param rhs the right operand
124 /// @tparam T type of the left operand
125 /// @tparam S type of the right operand
126 /// @return the logical xor
127 template <typename T, typename S>
128 constexpr bool operator()(T const& lhs, S const& rhs) const {
129 return (lhs && !rhs) || (!lhs && rhs);
130 }
131};
132} // namespace internal
133
134/// @brief this namespace contains all builtin operations supported by MPI.
135///
136/// You can either use them by passing their STL counterparts like \c std::plus<>, \c std::multiplies<> etc. or using
137/// the aliases \c kamping::ops::plus, \c kamping::ops::multiplies, \c kamping::ops::max(), ...
138/// You can either use them without a template parameter (\c std::plus<>) or explicitly specify the type (\c
139/// std::plus<int>). In the latter case, the type must match the datatype of the buffer the operation shall be applied
140/// to.
141namespace ops {
142
143/// @brief builtin maximum operation (aka `MPI_MAX`)
144template <typename T = void>
146
147/// @brief builtin minimum operation (aka `MPI_MIN`)
148template <typename T = void>
150
151/// @brief builtin summation operation (aka `MPI_SUM`)
152template <typename T = void>
153using plus = std::plus<T>;
154
155/// @brief builtin multiplication operation (aka `MPI_PROD`)
156template <typename T = void>
157using multiplies = std::multiplies<T>;
158
159/// @brief builtin logical and operation (aka `MPI_LAND`)
160template <typename T = void>
161using logical_and = std::logical_and<T>;
162
163/// @brief builtin bitwise and operation (aka `MPI_BAND`)
164template <typename T = void>
165using bit_and = std::bit_and<T>;
166
167/// @brief builtin logical or operation (aka `MPI_LOR`)
168template <typename T = void>
169using logical_or = std::logical_or<T>;
170
171/// @brief builtin bitwise or operation (aka `MPI_BOR`)
172template <typename T = void>
173using bit_or = std::bit_or<T>;
174
175/// @brief builtin logical xor operation (aka `MPI_LXOR`)
176template <typename T = void>
178
179/// @brief builtin bitwise xor operation (aka `MPI_BXOR`)
180template <typename T = void>
181using bit_xor = std::bit_xor<T>;
182
183/// @brief builtin null operation (aka `MPI_OP_NULL`)
184template <typename T = void>
185struct null {};
186
187namespace internal {
188/// @brief tag for a commutative reduce operation
190/// @brief tag for a non-commutative reduce operation
192/// @brief tag for a reduce operation without manually declared commutativity (this is only used
193/// internally for builtin reduce operations)
195} // namespace internal
196
197[[maybe_unused]] constexpr internal::commutative_tag commutative{}; ///< global tag for commutativity
198[[maybe_unused]] constexpr internal::non_commutative_tag non_commutative{}; ///< global tag for non-commutativity
199
200} // namespace ops
201
202namespace internal {
203
204#ifdef KAMPING_DOXYGEN_ONLY
205/// @brief Type trait for checking whether a functor is a builtin MPI reduction operation and query corresponding \c
206/// MPI_Op.
207///
208/// Example:
209/// @code
210/// is_builtin_mpi_op<kamping::ops::plus<>, int>::value // true
211/// is_builtin_mpi_op<kamping::ops::plus<>, int>::op() // MPI_SUM
212/// is_builtin_mpi_op<std::plus<>, int>::value // true
213/// is_builtin_mpi_op<std::plus<>, int>::op() // MPI_SUM
214/// is_builtin_mpi_op<std::minus<>, int>::value // false
215/// //is_builtin_mpi_op<std::minus<>, int>::op() // error: fails to compile because op is not defined
216/// @endcode
217///
218/// @tparam Op type of the operation
219/// @tparam Datatype type to apply the operation to
220template <typename Op, typename Datatype>
222 /// @brief \c true if the operation defined by \c Op is a builtin MPI operation for the type \c Datatype
223 ///
224 /// Note that this is only true if the \c MPI_Datatype corresponding to the C++ datatype \c Datatype supports the
225 /// operation according to the standard. If MPI supports the operation for this type, then this is true for functors
226 /// defined in \c kamping::ops and there corresponding type-aliased equivalents in the standard library.
227 static constexpr bool is_builtin;
228
229 /// @brief The identity of this operation applied on this datatype.
230 ///
231 /// The identity of a {value, operation} pair is the value for which the following two equation holds:
232 /// - `identity operation value = value`
233 /// - `value operation identity = value`
234 static constexpr T identity;
235
236 /// @brief get the MPI_Op for a builtin type
237 ///
238 /// This member is only defined if \c value is \c true. It can then be used to query the predefined constant of
239 /// type \c MPI_OP matching the functor defined by type \c Op, e.g. returns \c MPI_SUM if \c Op is \c
240 /// kamping::ops::plus<>.
241 /// @returns the builtin \c MPI_Op constant
242 static MPI_Op op();
243};
244#else
245
246template <typename Op, typename T, typename Enable = void>
248 static constexpr bool is_builtin = false;
249};
250
251template <typename T, typename S>
252struct mpi_operation_traits<
253 kamping::ops::max<S>,
254 T,
255 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
256 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
257 )>::type> {
258 static constexpr bool is_builtin = true;
259 static constexpr T identity = std::numeric_limits<T>::lowest();
260 static MPI_Op op() {
261 return MPI_MAX;
262 }
263};
264
265template <typename T, typename S>
266struct mpi_operation_traits<
267 kamping::ops::min<S>,
268 T,
269 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
270 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
271 )>::type> {
272 static constexpr bool is_builtin = true;
273 static constexpr T identity = std::numeric_limits<T>::max();
274 static MPI_Op op() {
275 return MPI_MIN;
276 }
277};
278
279template <typename T, typename S>
280struct mpi_operation_traits<
281 kamping::ops::plus<S>,
282 T,
283 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
284 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
285 || builtin_type<T>::category == TypeCategory::complex
286 )>::type> {
287 static constexpr bool is_builtin = true;
288 static constexpr T identity = 0;
289 static MPI_Op op() {
290 return MPI_SUM;
291 }
292};
293
294template <typename T, typename S>
295struct mpi_operation_traits<
296 kamping::ops::multiplies<S>,
297 T,
298 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
299 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::floating
300 || builtin_type<T>::category == TypeCategory::complex
301 )>::type> {
302 static constexpr bool is_builtin = true;
303 static constexpr T identity = 1;
304 static MPI_Op op() {
305 return MPI_PROD;
306 }
307};
308
309template <typename T, typename S>
310struct mpi_operation_traits<
311 kamping::ops::logical_and<S>,
312 T,
313 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
314 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
315 )>::type> {
316 static constexpr bool is_builtin = true;
317 static constexpr T identity = true;
318 static MPI_Op op() {
319 return MPI_LAND;
320 }
321};
322
323template <typename T, typename S>
324struct mpi_operation_traits<
325 kamping::ops::logical_or<S>,
326 T,
327 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
328 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
329 )>::type> {
330 static constexpr bool is_builtin = true;
331 static constexpr T identity = false;
332 static MPI_Op op() {
333 return MPI_LOR;
334 }
335};
336
337template <typename T, typename S>
338struct mpi_operation_traits<
339 kamping::ops::logical_xor<S>,
340 T,
341 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
342 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::logical
343 )>::type> {
344 static constexpr bool is_builtin = true;
345 static constexpr T identity = false;
346 static MPI_Op op() {
347 return MPI_LXOR;
348 }
349};
350
351template <typename T, typename S>
352struct mpi_operation_traits<
353 kamping::ops::bit_and<S>,
354 T,
355 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
356 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
357 )>::type> {
358 static constexpr bool is_builtin = true;
359 static constexpr T identity = ~(T{0});
360 static MPI_Op op() {
361 return MPI_BAND;
362 }
363};
364
365template <typename T, typename S>
366struct mpi_operation_traits<
367 kamping::ops::bit_or<S>,
368 T,
369 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
370 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
371 )>::type> {
372 static constexpr bool is_builtin = true;
373 static constexpr T identity = T{0};
374 static MPI_Op op() {
375 return MPI_BOR;
376 }
377};
378
379template <typename T, typename S>
380struct mpi_operation_traits<
381 kamping::ops::bit_xor<S>,
382 T,
383 typename std::enable_if<(std::is_same_v<S, void> || std::is_same_v<T, S>)&&(
384 builtin_type<T>::category == TypeCategory::integer || builtin_type<T>::category == TypeCategory::byte
385 )>::type> {
386 static constexpr bool is_builtin = true;
387 static constexpr T identity = 0;
388 static MPI_Op op() {
389 return MPI_BXOR;
390 }
391};
392#endif
393
394/// @todo support for MPI_MAXLOC and MPI_MINLOC
395
396/// @brief Helper function that maps an \c MPI_Op to the matching functor from \c kamping::ops. In case no function
397/// maps, the functor is called with \c kamping::ops::null<>{}.
398///
399/// @param op The operation.
400/// @param func The lambda to be called with the functor matching the \c MPI_Op, e.g. in the case of \c MPI_SUM we call
401/// \c func(kamping::ops::plus<>{}).
402template <typename Functor>
404 if (op == MPI_MAX) {
405 return func(ops::max<>{});
406 } else if (op == MPI_MIN) {
407 return func(ops::min<>{});
408 } else if (op == MPI_SUM) {
409 return func(ops::plus<>{});
410 } else if (op == MPI_PROD) {
411 return func(ops::multiplies<>{});
412 } else if (op == MPI_LAND) {
413 return func(ops::logical_and<>{});
414 } else if (op == MPI_LOR) {
415 return func(ops::logical_or<>{});
416 } else if (op == MPI_LXOR) {
417 return func(ops::logical_xor<>{});
418 } else if (op == MPI_BAND) {
419 return func(ops::bit_and<>{});
420 } else if (op == MPI_BOR) {
421 return func(ops::bit_or<>{});
422 } else if (op == MPI_BXOR) {
423 return func(ops::bit_xor<>{});
424 } else {
425 return func(ops::null<>{});
426 }
427}
428
429/// @brief type used by user-defined operations passed to \c MPI_Op_create
430using mpi_custom_operation_type = void (*)(void*, void*, int*, MPI_Datatype*);
431
432/// @brief Wrapper for a user defined reduction operation based on a functor object.
433///
434/// Internally, this creates an \c MPI_Op which is freed upon destruction.
435/// @tparam is_commutative whether the operation is commutative or not
436/// @tparam T the type to apply the operation to.
437/// @tparam Op type of the functor object to wrap
438template <bool is_commutative, typename T, typename Op>
440public:
441 static_assert(
442 std::is_default_constructible_v<Op>,
443 "This wrapper only works with default constructible functors, i.e., not with lambdas."
444 );
445
446 void operator=(UserOperationWrapper<is_commutative, T, Op>&) = delete;
447
448 void operator=(UserOperationWrapper<is_commutative, T, Op>&&) = delete;
449
450 /// @brief creates an MPI operation for the specified functor
451 /// @param op the functor to call for reduction.
452 /// this has to be a binary function applicable to two arguments of type \c T which return a result of type \c
453 /// T
455 static_assert(std::is_invocable_r_v<T, Op, T const&, T const&>, "Type of custom operation does not match.");
457 }
458
459 /// @brief wrapper around the provided functor which is called by MPI
460 static void execute(void* invec, void* inoutvec, int* len, MPI_Datatype* /*datatype*/) {
461 T* invec_ = static_cast<T*>(invec);
462 T* inoutvec_ = static_cast<T*>(inoutvec);
463 Op op{};
464 std::transform(invec_, invec_ + *len, inoutvec_, inoutvec_, op);
465 }
466
467 /// @brief Call the wrapped operation.
468 T operator()(T const& lhs, T const& rhs) const {
469 return _operation(lhs, rhs);
470 }
471
473 MPI_Op_free(&_mpi_op);
474 }
475
476 /// @returns the \c MPI_Op constructed for the provided functor.
477 ///
478 /// Do not free this operation manually, because the destructor calls it. Some MPI implementations silently
479 /// segfault if an \c MPI_Op is freed multiple times.
481 return _mpi_op;
482 }
483
484private:
485 Op _operation; ///< the functor to call for reduction
486 MPI_Op _mpi_op; ///< the \c MPI_Op referencing the user defined operation
487};
488
489/// @brief Wrapper for a user defined reduction operation based on a function pointer.
490///
491/// Internally, this creates an \c MPI_Op which is freed upon destruction.
492/// @tparam is_commutative whether the operation is commutative or not
493template <bool is_commutative>
495public:
497
498 /// @brief move assignment
500 this->_mpi_op = other_op._mpi_op;
501 this->_no_op = other_op._no_op;
502 other_op._no_op = true;
503 return *this;
504 }
505
507 /// @brief move constructor
509 this->_mpi_op = other_op._mpi_op;
510 this->_no_op = other_op._no_op;
511 other_op._no_op = true;
512 }
513 /// @brief creates an empty operation wrapper
515 _mpi_op = MPI_OP_NULL;
516 }
517 /// @brief creates an MPI operation for the specified function pointer
518 /// @param ptr the functor to call for reduction
519 /// this parameter must match the semantics of the function pointer passed to \c MPI_Op_create according to the
520 /// MPI standard.
522 KASSERT(ptr != nullptr);
523 MPI_Op_create(ptr, is_commutative, &_mpi_op);
524 }
525
527 if (!_no_op) {
528 MPI_Op_free(&_mpi_op);
529 }
530 }
531
532 /// @returns the \c MPI_Op constructed for the provided functor.
533 ///
534 /// Do not free this operation manually, because the destructor calls it. Some MPI implementations silently
535 /// segfault if an \c MPI_Op is freed multiple times.
537 return _mpi_op;
538 }
539
540private:
541 bool _no_op; ///< indicates if this operation is empty or was moved, so we can avoid freeing the same operation
542 ///< multiple times upon destruction
543 MPI_Op _mpi_op; ///< the \c MPI_Op referencing the user defined operation
544};
545
546#ifdef KAMPING_DOXYGEN_ONLY
547
548/// @brief Wraps an operation and translates it to a builtin \c MPI_Op or constructs a custom operation.
549/// @tparam T the argument type of the operation
550/// @tparam Op the type of the operation
551/// @tparam Commutative tag indicating if this type is commutative
552template <typename T, typename Op, typename Commutative>
554public:
555 /// @brief Constructs on operation wrapper
556 /// @param op the operation
557 /// maybe a function object a lambda or a \c std::function
558 /// @param commutative
559 /// May be any instance of \c commutative, \c or non_commutative. Passing \c undefined_commutative is only
560 /// supported for builtin operations.
562
563 static constexpr bool is_builtin; ///< indicates if this is a builtin operation
564 static constexpr bool commutative; ///< indicates if this operation is commutative
565
566 /// @returns the \c MPI_Op associated with this operation
568
569 /// @brief Call the underlying operation with the provided arguments.
570 T operator()(T const& lhs, T const& rhs) const;
571
572 /// @returns the identity element for this operation and data type.
574};
575
576#else
577
578template <typename T, typename Op, typename Commutative, class Enable = void>
579class ReduceOperation {
580 static_assert(
581 std::is_same_v<
583 kamping::ops::internal::
584 commutative_tag> || std::is_same_v<Commutative, kamping::ops::internal::non_commutative_tag>,
585 "For custom operations you have to specify whether they are commutative."
586 );
587
588public:
589 ReduceOperation(Op&& op, Commutative) : _operation(std::move(op)) {}
590 static constexpr bool is_builtin = false;
591 static constexpr bool commutative = std::is_same_v<Commutative, kamping::ops::internal::commutative_tag>;
592
593 T operator()(T const& lhs, T const& rhs) const {
594 return _operation(lhs, rhs);
595 }
596
597 MPI_Op op() {
598 return _operation.get_mpi_op();
599 }
600
601private:
602 UserOperationWrapper<commutative, T, Op> _operation;
603};
604
605/// @brief Wrapper for a native MPI_Op.
606template <typename T>
607class ReduceOperation<T, MPI_Op, ops::internal::undefined_commutative_tag, void> {
608public:
609 ReduceOperation(MPI_Op op, ops::internal::undefined_commutative_tag = {}) : _op(op) {}
610 static constexpr bool is_builtin = false; // set to false, because we can not decide that at compile time and don't
611 // need this information for a native \c MPI_Op
612
613 T operator()(T const& lhs, T const& rhs) const {
614 KASSERT(_op != MPI_OP_NULL, "Cannot call MPI_OP_NULL.");
615 T result;
616 internal::with_operation_functor(_op, [&result, lhs, rhs, this](auto operation) {
617 if constexpr (!std::is_same_v<decltype(operation), ops::null<> >) {
618 result = operation(lhs, rhs);
619 } else {
620 // ops::null indicates that this does not map to a functor, so we use the MPI_Op directly
621 result = rhs;
622 MPI_Reduce_local(&lhs, &result, 1, mpi_datatype<T>(), _op);
623 }
624 });
625 return result;
626 }
627
628 MPI_Op op() {
629 return _op;
630 }
631
632private:
633 MPI_Op _op;
634};
635
636template <typename T, typename Op, typename Commutative>
637class ReduceOperation<T, Op, Commutative, typename std::enable_if<mpi_operation_traits<Op, T>::is_builtin>::type> {
638 static_assert(
639 std::is_same_v<Commutative, kamping::ops::internal::undefined_commutative_tag>,
640 "For builtin operations you don't need to specify whether they are commutative."
641 );
642
643public:
644 ReduceOperation(Op&&, Commutative) {}
645 static constexpr bool is_builtin = true;
646 static constexpr bool commutative = true; // builtin operations are always commutative
647
648 MPI_Op op() {
650 }
651
652 T operator()(T const& lhs, T const& rhs) const {
653 return Op{}(lhs, rhs);
654 }
655
656 T identity() {
658 }
659};
660
661template <typename T, typename Op, typename Commutative>
662class ReduceOperation<T, Op, Commutative, typename std::enable_if<!std::is_default_constructible_v<Op> >::type> {
663 static_assert(
664 std::is_same_v<
665 Commutative,
666 kamping::ops::internal::
667 commutative_tag> || std::is_same_v<Commutative, kamping::ops::internal::non_commutative_tag>,
668 "For custom operations you have to specify whether they are commutative."
669 );
670
671public:
672 ReduceOperation(Op&& op, Commutative) : _op(op), _operation() {
673 // A lambda is may not be default constructed nor copied, so we need some hacks to deal with them.
674 // Because each lambda has a distinct type we initiate the static Op here and can access it from the static
675 // context of function pointer created afterwards.
676 static Op func = _op;
677
678 mpi_custom_operation_type ptr = [](void* invec, void* inoutvec, int* len, MPI_Datatype* /*datatype*/) {
679 T* invec_ = static_cast<T*>(invec);
680 T* inoutvec_ = static_cast<T*>(inoutvec);
681 std::transform(invec_, invec_ + *len, inoutvec_, inoutvec_, func);
682 };
683 _operation = {ptr};
684 }
685 static constexpr bool is_builtin = false;
686 static constexpr bool commutative = std::is_same_v<Commutative, kamping::ops::internal::commutative_tag>;
687
688 MPI_Op op() {
689 return _operation.get_mpi_op();
690 }
691
692 T operator()(T const& lhs, T const& rhs) const {
693 return _op(lhs, rhs);
694 }
695
696 T identity() {
697 return _operation.identity();
698 }
699
700private:
701 Op _op;
702 UserOperationPtrWrapper<commutative> _operation;
703};
704#endif
705} // namespace internal
706} // 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:553
ReduceOperation(Op &&op, Commutative commutative)
Constructs on operation wrapper.
static constexpr bool commutative
indicates if this operation is commutative
Definition mpi_ops.hpp:564
T operator()(T const &lhs, T const &rhs) const
Call the underlying operation with the provided arguments.
static constexpr bool is_builtin
indicates if this is a builtin operation
Definition mpi_ops.hpp:563
Wrapper for a user defined reduction operation based on a function pointer.
Definition mpi_ops.hpp:494
UserOperationPtrWrapper(mpi_custom_operation_type ptr)
creates an MPI operation for the specified function pointer
Definition mpi_ops.hpp:521
MPI_Op get_mpi_op()
Definition mpi_ops.hpp:536
UserOperationPtrWrapper(UserOperationPtrWrapper< is_commutative > &&other_op)
move constructor
Definition mpi_ops.hpp:508
UserOperationPtrWrapper< is_commutative > & operator=(UserOperationPtrWrapper< is_commutative > &&other_op)
move assignment
Definition mpi_ops.hpp:499
UserOperationPtrWrapper()
creates an empty operation wrapper
Definition mpi_ops.hpp:514
Wrapper for a user defined reduction operation based on a functor object.
Definition mpi_ops.hpp:439
static void execute(void *invec, void *inoutvec, int *len, MPI_Datatype *)
wrapper around the provided functor which is called by MPI
Definition mpi_ops.hpp:460
MPI_Op get_mpi_op()
Definition mpi_ops.hpp:480
T operator()(T const &lhs, T const &rhs) const
Call the wrapped operation.
Definition mpi_ops.hpp:468
UserOperationWrapper(Op &&op)
creates an MPI operation for the specified functor
Definition mpi_ops.hpp:454
@ op
Tag used to represent a reduce operation in a MPI call.
Utility that maps C++ types to types that can be understood by MPI.
void(*)(void *, void *, int *, MPI_Datatype *) mpi_custom_operation_type
type used by user-defined operations passed to MPI_Op_create
Definition mpi_ops.hpp:430
auto with_operation_functor(MPI_Op op, Functor &&func)
Helper function that maps an MPI_Op to the matching functor from kamping::ops. In case no function ma...
Definition mpi_ops.hpp:403
std::logical_and< T > logical_and
builtin logical and operation (aka MPI_LAND)
Definition mpi_ops.hpp:161
constexpr internal::commutative_tag commutative
global tag for commutativity
Definition mpi_ops.hpp:197
kamping::internal::logical_xor_impl< T > logical_xor
builtin logical xor operation (aka MPI_LXOR)
Definition mpi_ops.hpp:177
std::bit_or< T > bit_or
builtin bitwise or operation (aka MPI_BOR)
Definition mpi_ops.hpp:173
std::logical_or< T > logical_or
builtin logical or operation (aka MPI_LOR)
Definition mpi_ops.hpp:169
kamping::internal::min_impl< T > min
builtin minimum operation (aka MPI_MIN)
Definition mpi_ops.hpp:149
std::bit_and< T > bit_and
builtin bitwise and operation (aka MPI_BAND)
Definition mpi_ops.hpp:165
std::bit_xor< T > bit_xor
builtin bitwise xor operation (aka MPI_BXOR)
Definition mpi_ops.hpp:181
std::plus< T > plus
builtin summation operation (aka MPI_SUM)
Definition mpi_ops.hpp:153
constexpr internal::non_commutative_tag non_commutative
global tag for non-commutativity
Definition mpi_ops.hpp:198
std::multiplies< T > multiplies
builtin multiplication operation (aka MPI_PROD)
Definition mpi_ops.hpp:157
STL namespace.
constexpr bool operator()(T const &lhs, S const &rhs) const
Returns the logical xor of the two parameters.
Definition mpi_ops.hpp:128
Wrapper struct for logical xor, as the standard library does not provided a function object for it.
Definition mpi_ops.hpp:106
constexpr bool operator()(T const &lhs, T const &rhs) const
Returns the logical xor of the two parameters.
Definition mpi_ops.hpp:111
constexpr auto operator()(T const &lhs, T const &rhs) const
Returns the maximum of the two parameters.
Definition mpi_ops.hpp:61
Wrapper struct for std::max.
Definition mpi_ops.hpp:38
constexpr T operator()(T const &lhs, T const &rhs) const
Returns the maximum of the two parameters.
Definition mpi_ops.hpp:43
constexpr auto operator()(T const &lhs, T const &rhs) const
Returns the maximum of the two parameters.
Definition mpi_ops.hpp:95
Wrapper struct for std::min.
Definition mpi_ops.hpp:74
constexpr T operator()(T const &lhs, T const &rhs) const
returns the maximum of the two parameters
Definition mpi_ops.hpp:79
Type trait for checking whether a functor is a builtin MPI reduction operation and query correspondin...
Definition mpi_ops.hpp:221
static constexpr T identity
The identity of this operation applied on this datatype.
Definition mpi_ops.hpp:234
static constexpr bool is_builtin
true if the operation defined by Op is a builtin MPI operation for the type Datatype
Definition mpi_ops.hpp:227
static MPI_Op op()
get the MPI_Op for a builtin type
tag for a commutative reduce operation
Definition mpi_ops.hpp:189
tag for a non-commutative reduce operation
Definition mpi_ops.hpp:191
tag for a reduce operation without manually declared commutativity (this is only used internally for ...
Definition mpi_ops.hpp:194
builtin null operation (aka MPI_OP_NULL)
Definition mpi_ops.hpp:185