5 #ifndef GKO_PUBLIC_CORE_BASE_MPI_HPP_
6 #define GKO_PUBLIC_CORE_BASE_MPI_HPP_
10 #include <type_traits>
14 #include <ginkgo/config.hpp>
15 #include <ginkgo/core/base/exception.hpp>
16 #include <ginkgo/core/base/exception_helpers.hpp>
17 #include <ginkgo/core/base/executor.hpp>
18 #include <ginkgo/core/base/types.hpp>
19 #include <ginkgo/core/base/utils_helper.hpp>
29 namespace experimental {
44 #if GINKGO_HAVE_GPU_AWARE_MPI
62 #define GKO_REGISTER_MPI_TYPE(input_type, mpi_type) \
64 struct type_impl<input_type> { \
65 static MPI_Datatype get_type() { return mpi_type; } \
80 GKO_REGISTER_MPI_TYPE(
char, MPI_CHAR);
81 GKO_REGISTER_MPI_TYPE(
unsigned char, MPI_UNSIGNED_CHAR);
82 GKO_REGISTER_MPI_TYPE(
unsigned, MPI_UNSIGNED);
83 GKO_REGISTER_MPI_TYPE(
int, MPI_INT);
84 GKO_REGISTER_MPI_TYPE(
unsigned short, MPI_UNSIGNED_SHORT);
85 GKO_REGISTER_MPI_TYPE(
unsigned long, MPI_UNSIGNED_LONG);
86 GKO_REGISTER_MPI_TYPE(
long, MPI_LONG);
87 GKO_REGISTER_MPI_TYPE(
long long, MPI_LONG_LONG_INT);
88 GKO_REGISTER_MPI_TYPE(
unsigned long long, MPI_UNSIGNED_LONG_LONG);
89 GKO_REGISTER_MPI_TYPE(
float, MPI_FLOAT);
90 GKO_REGISTER_MPI_TYPE(
double, MPI_DOUBLE);
91 GKO_REGISTER_MPI_TYPE(
long double, MPI_LONG_DOUBLE);
92 GKO_REGISTER_MPI_TYPE(std::complex<float>, MPI_C_FLOAT_COMPLEX);
93 GKO_REGISTER_MPI_TYPE(std::complex<double>, MPI_C_DOUBLE_COMPLEX);
112 GKO_ASSERT_NO_MPI_ERRORS(MPI_Type_contiguous(count, old_type, &type_));
113 GKO_ASSERT_NO_MPI_ERRORS(MPI_Type_commit(&type_));
138 *
this = std::move(other);
150 if (
this != &other) {
151 this->type_ = std::exchange(other.type_, MPI_DATATYPE_NULL);
161 if (type_ != MPI_DATATYPE_NULL) {
162 MPI_Type_free(&type_);
171 MPI_Datatype
get()
const {
return type_; }
183 serialized = MPI_THREAD_SERIALIZED,
184 funneled = MPI_THREAD_FUNNELED,
185 single = MPI_THREAD_SINGLE,
186 multiple = MPI_THREAD_MULTIPLE
201 static bool is_finalized()
204 GKO_ASSERT_NO_MPI_ERRORS(MPI_Finalized(&flag));
208 static bool is_initialized()
211 GKO_ASSERT_NO_MPI_ERRORS(MPI_Initialized(&flag));
231 const thread_type thread_t = thread_type::serialized)
233 this->required_thread_support_ = static_cast<int>(thread_t);
234 GKO_ASSERT_NO_MPI_ERRORS(
235 MPI_Init_thread(&argc, &argv, this->required_thread_support_,
236 &(this->provided_thread_support_)));
250 int required_thread_support_;
251 int provided_thread_support_;
264 using pointer = MPI_Comm*;
265 void operator()(pointer comm)
const
267 GKO_ASSERT(*comm != MPI_COMM_NULL);
268 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_free(comm));
291 MPI_Status*
get() {
return &this->status_; }
303 template <
typename T>
337 this->req_ = std::exchange(o.req_, MPI_REQUEST_NULL);
344 if (req_ != MPI_REQUEST_NULL) {
345 if (MPI_Request_free(&req_) != MPI_SUCCESS) {
357 MPI_Request*
get() {
return &this->req_; }
368 GKO_ASSERT_NO_MPI_ERRORS(MPI_Wait(&req_,
status.
get()));
385 inline std::vector<status>
wait_all(std::vector<request>& req)
387 std::vector<status> stat;
388 for (std::size_t i = 0; i < req.size(); ++i) {
389 stat.emplace_back(req[i].wait());
422 : comm_(), force_host_buffer_(force_host_buffer)
424 this->comm_.reset(
new MPI_Comm(comm));
438 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_split(comm, color, key, &comm_out));
439 this->comm_.reset(
new MPI_Comm(comm_out), comm_deleter{});
453 GKO_ASSERT_NO_MPI_ERRORS(
454 MPI_Comm_split(comm.
get(), color, key, &comm_out));
455 this->comm_.reset(
new MPI_Comm(comm_out), comm_deleter{});
463 const MPI_Comm&
get()
const {
return *(this->comm_.get()); }
465 bool force_host_buffer()
const {
return force_host_buffer_; }
472 int size()
const {
return get_num_ranks(); }
479 int rank()
const {
return get_my_rank(); };
495 return compare(rhs.get());
511 GKO_ASSERT_NO_MPI_ERRORS(MPI_Barrier(this->
get()));
527 template <
typename SendType>
528 void send(std::shared_ptr<const Executor> exec,
const SendType* send_buffer,
529 const int send_count,
const int destination_rank,
530 const int send_tag)
const
532 auto guard = exec->get_scoped_device_id_guard();
533 GKO_ASSERT_NO_MPI_ERRORS(
535 destination_rank, send_tag, this->
get()));
554 template <
typename SendType>
556 const SendType* send_buffer,
const int send_count,
557 const int destination_rank,
const int send_tag)
const
559 auto guard = exec->get_scoped_device_id_guard();
561 GKO_ASSERT_NO_MPI_ERRORS(
563 destination_rank, send_tag, this->
get(), req.
get()));
582 template <
typename RecvType>
583 status recv(std::shared_ptr<const Executor> exec, RecvType* recv_buffer,
584 const int recv_count,
const int source_rank,
585 const int recv_tag)
const
587 auto guard = exec->get_scoped_device_id_guard();
589 GKO_ASSERT_NO_MPI_ERRORS(
591 source_rank, recv_tag, this->
get(), st.
get()));
610 template <
typename RecvType>
612 const int recv_count,
const int source_rank,
613 const int recv_tag)
const
615 auto guard = exec->get_scoped_device_id_guard();
617 GKO_ASSERT_NO_MPI_ERRORS(
619 source_rank, recv_tag, this->
get(), req.
get()));
635 template <
typename BroadcastType>
636 void broadcast(std::shared_ptr<const Executor> exec, BroadcastType* buffer,
637 int count,
int root_rank)
const
639 auto guard = exec->get_scoped_device_id_guard();
640 GKO_ASSERT_NO_MPI_ERRORS(MPI_Bcast(buffer, count,
642 root_rank, this->
get()));
660 template <
typename BroadcastType>
662 BroadcastType* buffer,
int count,
int root_rank)
const
664 auto guard = exec->get_scoped_device_id_guard();
666 GKO_ASSERT_NO_MPI_ERRORS(
668 root_rank, this->
get(), req.
get()));
686 template <
typename ReduceType>
687 void reduce(std::shared_ptr<const Executor> exec,
688 const ReduceType* send_buffer, ReduceType* recv_buffer,
689 int count, MPI_Op operation,
int root_rank)
const
691 auto guard = exec->get_scoped_device_id_guard();
692 GKO_ASSERT_NO_MPI_ERRORS(MPI_Reduce(send_buffer, recv_buffer, count,
694 operation, root_rank, this->
get()));
713 template <
typename ReduceType>
715 const ReduceType* send_buffer, ReduceType* recv_buffer,
716 int count, MPI_Op operation,
int root_rank)
const
718 auto guard = exec->get_scoped_device_id_guard();
720 GKO_ASSERT_NO_MPI_ERRORS(MPI_Ireduce(
722 operation, root_rank, this->
get(), req.
get()));
739 template <
typename ReduceType>
741 ReduceType* recv_buffer,
int count, MPI_Op operation)
const
743 auto guard = exec->get_scoped_device_id_guard();
744 GKO_ASSERT_NO_MPI_ERRORS(MPI_Allreduce(
746 operation, this->
get()));
764 template <
typename ReduceType>
766 ReduceType* recv_buffer,
int count,
767 MPI_Op operation)
const
769 auto guard = exec->get_scoped_device_id_guard();
771 GKO_ASSERT_NO_MPI_ERRORS(MPI_Iallreduce(
773 operation, this->
get(), req.
get()));
791 template <
typename ReduceType>
793 const ReduceType* send_buffer, ReduceType* recv_buffer,
794 int count, MPI_Op operation)
const
796 auto guard = exec->get_scoped_device_id_guard();
797 GKO_ASSERT_NO_MPI_ERRORS(MPI_Allreduce(
799 operation, this->
get()));
818 template <
typename ReduceType>
820 const ReduceType* send_buffer, ReduceType* recv_buffer,
821 int count, MPI_Op operation)
const
823 auto guard = exec->get_scoped_device_id_guard();
825 GKO_ASSERT_NO_MPI_ERRORS(MPI_Iallreduce(
827 operation, this->
get(), req.
get()));
847 template <
typename SendType,
typename RecvType>
848 void gather(std::shared_ptr<const Executor> exec,
849 const SendType* send_buffer,
const int send_count,
850 RecvType* recv_buffer,
const int recv_count,
853 auto guard = exec->get_scoped_device_id_guard();
854 GKO_ASSERT_NO_MPI_ERRORS(
857 root_rank, this->
get()));
879 template <
typename SendType,
typename RecvType>
881 const SendType* send_buffer,
const int send_count,
882 RecvType* recv_buffer,
const int recv_count,
885 auto guard = exec->get_scoped_device_id_guard();
887 GKO_ASSERT_NO_MPI_ERRORS(MPI_Igather(
912 template <
typename SendType,
typename RecvType>
913 void gather_v(std::shared_ptr<const Executor> exec,
914 const SendType* send_buffer,
const int send_count,
915 RecvType* recv_buffer,
const int* recv_counts,
916 const int* displacements,
int root_rank)
const
918 auto guard = exec->get_scoped_device_id_guard();
919 GKO_ASSERT_NO_MPI_ERRORS(MPI_Gatherv(
921 recv_buffer, recv_counts, displacements,
945 template <
typename SendType,
typename RecvType>
947 const SendType* send_buffer,
const int send_count,
948 RecvType* recv_buffer,
const int* recv_counts,
949 const int* displacements,
int root_rank)
const
951 auto guard = exec->get_scoped_device_id_guard();
953 GKO_ASSERT_NO_MPI_ERRORS(MPI_Igatherv(
955 recv_buffer, recv_counts, displacements,
976 template <
typename SendType,
typename RecvType>
978 const SendType* send_buffer,
const int send_count,
979 RecvType* recv_buffer,
const int recv_count)
const
981 auto guard = exec->get_scoped_device_id_guard();
982 GKO_ASSERT_NO_MPI_ERRORS(MPI_Allgather(
1006 template <
typename SendType,
typename RecvType>
1008 const SendType* send_buffer,
const int send_count,
1009 RecvType* recv_buffer,
const int recv_count)
const
1011 auto guard = exec->get_scoped_device_id_guard();
1013 GKO_ASSERT_NO_MPI_ERRORS(MPI_Iallgather(
1016 this->
get(), req.
get()));
1035 template <
typename SendType,
typename RecvType>
1036 void scatter(std::shared_ptr<const Executor> exec,
1037 const SendType* send_buffer,
const int send_count,
1038 RecvType* recv_buffer,
const int recv_count,
1039 int root_rank)
const
1041 auto guard = exec->get_scoped_device_id_guard();
1042 GKO_ASSERT_NO_MPI_ERRORS(MPI_Scatter(
1066 template <
typename SendType,
typename RecvType>
1068 const SendType* send_buffer,
const int send_count,
1069 RecvType* recv_buffer,
const int recv_count,
1070 int root_rank)
const
1072 auto guard = exec->get_scoped_device_id_guard();
1074 GKO_ASSERT_NO_MPI_ERRORS(MPI_Iscatter(
1077 this->
get(), req.
get()));
1099 template <
typename SendType,
typename RecvType>
1101 const SendType* send_buffer,
const int* send_counts,
1102 const int* displacements, RecvType* recv_buffer,
1103 const int recv_count,
int root_rank)
const
1105 auto guard = exec->get_scoped_device_id_guard();
1106 GKO_ASSERT_NO_MPI_ERRORS(MPI_Scatterv(
1107 send_buffer, send_counts, displacements,
1132 template <
typename SendType,
typename RecvType>
1134 const SendType* send_buffer,
const int* send_counts,
1135 const int* displacements, RecvType* recv_buffer,
1136 const int recv_count,
int root_rank)
const
1138 auto guard = exec->get_scoped_device_id_guard();
1140 GKO_ASSERT_NO_MPI_ERRORS(
1141 MPI_Iscatterv(send_buffer, send_counts, displacements,
1144 root_rank, this->
get(), req.
get()));
1164 template <
typename RecvType>
1165 void all_to_all(std::shared_ptr<const Executor> exec, RecvType* recv_buffer,
1166 const int recv_count)
const
1168 auto guard = exec->get_scoped_device_id_guard();
1169 GKO_ASSERT_NO_MPI_ERRORS(MPI_Alltoall(
1193 template <
typename RecvType>
1195 RecvType* recv_buffer,
const int recv_count)
const
1197 auto guard = exec->get_scoped_device_id_guard();
1199 GKO_ASSERT_NO_MPI_ERRORS(MPI_Ialltoall(
1202 this->
get(), req.
get()));
1222 template <
typename SendType,
typename RecvType>
1224 const SendType* send_buffer,
const int send_count,
1225 RecvType* recv_buffer,
const int recv_count)
const
1227 auto guard = exec->get_scoped_device_id_guard();
1228 GKO_ASSERT_NO_MPI_ERRORS(MPI_Alltoall(
1252 template <
typename SendType,
typename RecvType>
1254 const SendType* send_buffer,
const int send_count,
1255 RecvType* recv_buffer,
const int recv_count)
const
1257 auto guard = exec->get_scoped_device_id_guard();
1259 GKO_ASSERT_NO_MPI_ERRORS(MPI_Ialltoall(
1262 this->
get(), req.
get()));
1285 template <
typename SendType,
typename RecvType>
1287 const SendType* send_buffer,
const int* send_counts,
1288 const int* send_offsets, RecvType* recv_buffer,
1289 const int* recv_counts,
const int* recv_offsets)
const
1291 this->
all_to_all_v(std::move(exec), send_buffer, send_counts,
1293 recv_buffer, recv_counts, recv_offsets,
1313 const void* send_buffer,
const int* send_counts,
1314 const int* send_offsets, MPI_Datatype send_type,
1315 void* recv_buffer,
const int* recv_counts,
1316 const int* recv_offsets, MPI_Datatype recv_type)
const
1318 auto guard = exec->get_scoped_device_id_guard();
1319 GKO_ASSERT_NO_MPI_ERRORS(MPI_Alltoallv(
1320 send_buffer, send_counts, send_offsets, send_type, recv_buffer,
1321 recv_counts, recv_offsets, recv_type, this->
get()));
1344 const void* send_buffer,
const int* send_counts,
1345 const int* send_offsets, MPI_Datatype send_type,
1346 void* recv_buffer,
const int* recv_counts,
1347 const int* recv_offsets,
1348 MPI_Datatype recv_type)
const
1350 auto guard = exec->get_scoped_device_id_guard();
1352 GKO_ASSERT_NO_MPI_ERRORS(MPI_Ialltoallv(
1353 send_buffer, send_counts, send_offsets, send_type, recv_buffer,
1354 recv_counts, recv_offsets, recv_type, this->
get(), req.
get()));
1378 template <
typename SendType,
typename RecvType>
1380 const SendType* send_buffer,
const int* send_counts,
1381 const int* send_offsets, RecvType* recv_buffer,
1382 const int* recv_counts,
1383 const int* recv_offsets)
const
1386 std::move(exec), send_buffer, send_counts, send_offsets,
1405 template <
typename ScanType>
1406 void scan(std::shared_ptr<const Executor> exec,
const ScanType* send_buffer,
1407 ScanType* recv_buffer,
int count, MPI_Op operation)
const
1409 auto guard = exec->get_scoped_device_id_guard();
1410 GKO_ASSERT_NO_MPI_ERRORS(MPI_Scan(send_buffer, recv_buffer, count,
1412 operation, this->
get()));
1431 template <
typename ScanType>
1433 const ScanType* send_buffer, ScanType* recv_buffer,
1434 int count, MPI_Op operation)
const
1436 auto guard = exec->get_scoped_device_id_guard();
1438 GKO_ASSERT_NO_MPI_ERRORS(MPI_Iscan(send_buffer, recv_buffer, count,
1440 operation, this->
get(), req.
get()));
1445 std::shared_ptr<MPI_Comm> comm_;
1446 bool force_host_buffer_;
1448 int get_my_rank()
const
1451 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_rank(
get(), &my_rank));
1455 int get_node_local_rank()
const
1457 MPI_Comm local_comm;
1459 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_split_type(
1460 this->
get(), MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &local_comm));
1461 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_rank(local_comm, &
rank));
1462 MPI_Comm_free(&local_comm);
1466 int get_num_ranks()
const
1469 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_size(this->
get(), &size));
1473 bool compare(
const MPI_Comm& other)
const
1476 GKO_ASSERT_NO_MPI_ERRORS(MPI_Comm_compare(
get(), other, &flag));
1477 return flag == MPI_IDENT;
1487 const communicator& comm);
1506 template <
typename ValueType>
1512 enum class create_type { allocate = 1, create = 2, dynamic_create = 3 };
1534 window(
window&& other) : window_{std::exchange(other.window_, MPI_WIN_NULL)}
1545 window_ = std::exchange(other.window_, MPI_WIN_NULL);
1560 window(std::shared_ptr<const Executor> exec, ValueType* base,
int num_elems,
1561 const communicator& comm,
const int disp_unit =
sizeof(ValueType),
1562 MPI_Info input_info = MPI_INFO_NULL,
1565 auto guard = exec->get_scoped_device_id_guard();
1566 unsigned size = num_elems *
sizeof(ValueType);
1567 if (c_type == create_type::create) {
1568 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_create(
1569 base, size, disp_unit, input_info, comm.
get(), &this->window_));
1570 }
else if (c_type == create_type::dynamic_create) {
1571 GKO_ASSERT_NO_MPI_ERRORS(
1572 MPI_Win_create_dynamic(input_info, comm.
get(), &this->window_));
1573 }
else if (c_type == create_type::allocate) {
1574 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_allocate(
1575 size, disp_unit, input_info, comm.
get(), base, &this->window_));
1577 GKO_NOT_IMPLEMENTED;
1596 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_fence(assert, this->window_));
1608 int assert = 0)
const
1610 if (lock_t == lock_type::shared) {
1611 GKO_ASSERT_NO_MPI_ERRORS(
1612 MPI_Win_lock(MPI_LOCK_SHARED, rank, assert, this->window_));
1613 }
else if (lock_t == lock_type::exclusive) {
1614 GKO_ASSERT_NO_MPI_ERRORS(
1615 MPI_Win_lock(MPI_LOCK_EXCLUSIVE, rank, assert, this->window_));
1617 GKO_NOT_IMPLEMENTED;
1629 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_unlock(rank, this->window_));
1640 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_lock_all(assert, this->window_));
1649 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_unlock_all(this->window_));
1660 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_flush(rank, this->window_));
1671 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_flush_local(rank, this->window_));
1680 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_flush_all(this->window_));
1689 GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_flush_local_all(this->window_));
1695 void sync()
const { GKO_ASSERT_NO_MPI_ERRORS(MPI_Win_sync(this->window_)); }
1702 if (this->window_ && this->window_ != MPI_WIN_NULL) {
1703 MPI_Win_free(&this->window_);
1717 template <
typename PutType>
1718 void put(std::shared_ptr<const Executor> exec,
const PutType* origin_buffer,
1719 const int origin_count,
const int target_rank,
1720 const unsigned int target_disp,
const int target_count)
const
1722 auto guard = exec->get_scoped_device_id_guard();
1723 GKO_ASSERT_NO_MPI_ERRORS(
1725 target_rank, target_disp, target_count,
1741 template <
typename PutType>
1743 const PutType* origin_buffer,
const int origin_count,
1744 const int target_rank,
const unsigned int target_disp,
1745 const int target_count)
const
1747 auto guard = exec->get_scoped_device_id_guard();
1749 GKO_ASSERT_NO_MPI_ERRORS(MPI_Rput(
1751 target_rank, target_disp, target_count,
1767 template <
typename PutType>
1769 const PutType* origin_buffer,
const int origin_count,
1770 const int target_rank,
const unsigned int target_disp,
1771 const int target_count, MPI_Op operation)
const
1773 auto guard = exec->get_scoped_device_id_guard();
1774 GKO_ASSERT_NO_MPI_ERRORS(MPI_Accumulate(
1776 target_rank, target_disp, target_count,
1793 template <
typename PutType>
1795 const PutType* origin_buffer,
const int origin_count,
1796 const int target_rank,
const unsigned int target_disp,
1797 const int target_count, MPI_Op operation)
const
1799 auto guard = exec->get_scoped_device_id_guard();
1801 GKO_ASSERT_NO_MPI_ERRORS(MPI_Raccumulate(
1803 target_rank, target_disp, target_count,
1819 template <
typename GetType>
1820 void get(std::shared_ptr<const Executor> exec, GetType* origin_buffer,
1821 const int origin_count,
const int target_rank,
1822 const unsigned int target_disp,
const int target_count)
const
1824 auto guard = exec->get_scoped_device_id_guard();
1825 GKO_ASSERT_NO_MPI_ERRORS(
1827 target_rank, target_disp, target_count,
1843 template <
typename GetType>
1844 request r_get(std::shared_ptr<const Executor> exec, GetType* origin_buffer,
1845 const int origin_count,
const int target_rank,
1846 const unsigned int target_disp,
const int target_count)
const
1848 auto guard = exec->get_scoped_device_id_guard();
1850 GKO_ASSERT_NO_MPI_ERRORS(MPI_Rget(
1852 target_rank, target_disp, target_count,
1870 template <
typename GetType>
1872 GetType* origin_buffer,
const int origin_count,
1873 GetType* result_buffer,
const int result_count,
1874 const int target_rank,
const unsigned int target_disp,
1875 const int target_count, MPI_Op operation)
const
1877 auto guard = exec->get_scoped_device_id_guard();
1878 GKO_ASSERT_NO_MPI_ERRORS(MPI_Get_accumulate(
1881 target_rank, target_disp, target_count,
1900 template <
typename GetType>
1902 GetType* origin_buffer,
const int origin_count,
1903 GetType* result_buffer,
const int result_count,
1904 const int target_rank,
1905 const unsigned int target_disp,
1906 const int target_count, MPI_Op operation)
const
1908 auto guard = exec->get_scoped_device_id_guard();
1910 GKO_ASSERT_NO_MPI_ERRORS(MPI_Rget_accumulate(
1913 target_rank, target_disp, target_count,
1929 template <
typename GetType>
1931 GetType* origin_buffer, GetType* result_buffer,
1932 const int target_rank,
const unsigned int target_disp,
1933 MPI_Op operation)
const
1935 auto guard = exec->get_scoped_device_id_guard();
1936 GKO_ASSERT_NO_MPI_ERRORS(MPI_Fetch_and_op(
1938 target_rank, target_disp, operation, this->
get_window()));
1951 #endif // GKO_HAVE_MPI
1954 #endif // GKO_PUBLIC_CORE_BASE_MPI_HPP_