version 3.11-dev
Loading...
Searching...
No Matches
nonblockingsparseexchange.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
18#ifndef DUMUX_PARALLEL_NONBLOCKING_SPARSE_EXCHANGE_HH
19#define DUMUX_PARALLEL_NONBLOCKING_SPARSE_EXCHANGE_HH
20
21#include <vector>
22#include <cstddef>
23#include <cassert>
24
25#if HAVE_MPI
26#include <mpi.h>
27#endif
28
29namespace Dumux::Detail {
30
45template<class Communication>
46std::vector<char> exchangeSparse(const Communication& comm,
47 std::vector<std::vector<char>>& sendBufs,
48 [[maybe_unused]] int tag = 7373)
49{
50 std::vector<char> recvBuf;
51
52#if HAVE_MPI
53 const int myRank = comm.rank();
54 const int numProc = comm.size();
55 if (numProc <= 1)
56 return recvBuf;
57
58 // Dune::Communication<MPI_Comm> exposes operator MPI_Comm() const
59 const MPI_Comm mpiComm = comm;
60
61 // 1. Post a nonblocking synchronous send for every non-empty message. An Issend
62 // completes locally only once the matching receive has been posted remotely,
63 // which is what lets the barrier below detect global completion.
64 std::vector<MPI_Request> sendRequests;
65 sendRequests.reserve(numProc);
66 for (int to = 0; to < numProc; ++to)
67 {
68 if (to == myRank || sendBufs[to].empty())
69 continue;
70 sendRequests.emplace_back();
71 MPI_Issend(sendBufs[to].data(), static_cast<int>(sendBufs[to].size()),
72 MPI_BYTE, to, tag, mpiComm, &sendRequests.back());
73 }
74
75 // Receive (via matched probe) every message that can currently be matched,
76 // appending its bytes to the receive buffer.
77 const auto receivePending = [&]
78 {
79 int flag = 0;
80 MPI_Message message;
81 MPI_Status status;
82 MPI_Improbe(MPI_ANY_SOURCE, tag, mpiComm, &flag, &message, &status);
83 while (flag)
84 {
85 int count = 0;
86 MPI_Get_count(&status, MPI_BYTE, &count);
87 const std::size_t offset = recvBuf.size();
88 recvBuf.resize(offset + static_cast<std::size_t>(count));
89 MPI_Mrecv(recvBuf.data() + offset, count, MPI_BYTE, &message, MPI_STATUS_IGNORE);
90 MPI_Improbe(MPI_ANY_SOURCE, tag, mpiComm, &flag, &message, &status);
91 }
92 };
93
94 // 2./3. Nonblocking consensus: keep draining incoming messages. Once all of our
95 // own sends have been matched we enter a nonblocking barrier; its completion
96 // means every rank's sends have been matched globally, so we are done.
97 MPI_Request barrierRequest = MPI_REQUEST_NULL;
98 bool barrierActive = false;
99 bool done = false;
100 while (!done)
101 {
102 receivePending();
103
104 if (barrierActive)
105 {
106 int barrierDone = 0;
107 MPI_Test(&barrierRequest, &barrierDone, MPI_STATUS_IGNORE);
108 done = (barrierDone != 0);
109 }
110 else
111 {
112 int sendsDone = 0;
113 MPI_Testall(static_cast<int>(sendRequests.size()),
114 sendRequests.data(), &sendsDone, MPI_STATUSES_IGNORE);
115 if (sendsDone)
116 {
117 MPI_Ibarrier(mpiComm, &barrierRequest);
118 barrierActive = true;
119 }
120 }
121 }
122#else
123 assert(comm.size() == 1 && "Sparse data exchange between multiple ranks requires MPI");
124 (void)sendBufs;
125#endif
126
127 return recvBuf;
128}
129
130} // end namespace Dumux::Detail
131
132#endif
std::vector< char > exchangeSparse(const Communication &comm, std::vector< std::vector< char > > &sendBufs, int tag=7373)
Exchange variable-sized byte messages with a sparse set of peer processes.
Definition nonblockingsparseexchange.hh:46
Definition cvfelocalresidual.hh:25