TRIQS/mpi
2.0.0
C++ interface to MPI
Toggle main menu visibility
Loading...
Searching...
No Matches
monitor.hpp
Go to the documentation of this file.
1
// Copyright (c) 2020-2024 Simons Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0.txt
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
// Authors: Philipp Dumitrescu, Thomas Hahn, Olivier Parcollet, Nils Wentzell
16
21
22
#pragma once
23
24
#include "
./communicator.hpp
"
25
#include "
./macros.hpp
"
26
#include "
./utils.hpp
"
27
28
#include <mpi.h>
29
30
#include <vector>
31
#include <unistd.h>
32
33
namespace
mpi {
34
54
class
monitor
{
55
// Future struct for non-blocking MPI communication.
56
struct
future {
57
// MPI request of the non-blocking MPI call.
58
MPI_Request request{};
59
60
// 0 means that no event has occurred, 1 means that an event has occurred.
61
int
event
= 0;
62
};
63
64
public
:
78
monitor
(
mpi::communicator
c) : comm_(c.duplicate()) {
79
if
(comm_.rank() == 0) {
80
root_futures_.resize(c.size() - 1);
81
for (int rank = 1; rank < c.size(); ++rank) {
82
check_mpi_call(MPI_Irecv(&(root_futures_[rank - 1].event), 1, MPI_INT, rank, rank, comm_.get(), &(root_futures_[rank - 1].request)),
83
"MPI_Irecv"
);
84
}
85
}
else
{
86
check_mpi_call
(MPI_Ibcast(&any_event_, 1, MPI_INT, 0, comm_.
get
(), &req_ibcast_any_),
"MPI_Ibcast"
);
87
check_mpi_call
(MPI_Ibcast(&all_events_, 1, MPI_INT, 0, comm_.
get
(), &req_ibcast_all_),
"MPI_Ibcast"
);
88
}
89
}
90
92
monitor
(
monitor
const
&) =
delete
;
93
95
monitor
&
operator=
(
monitor
const
&) =
delete
;
96
98
~monitor
() {
finalize_communications
(); }
99
113
void
report_local_event
() {
114
// prevent sending multiple signals
115
if
(local_event_ or finalized_) {
return
; }
116
117
// a local event has occurred
118
local_event_ = 1;
119
if
(comm_.rank() == 0) {
120
// on root process, check all other nodes and perform necessary broadcasts
121
root_check_nodes_and_bcast();
122
}
else
{
123
// on non-root processes, let the root process know about the local event
124
check_mpi_call
(MPI_Isend(&local_event_, 1, MPI_INT, 0, comm_.rank(), comm_.get(), &req_isent_),
"MPI_Isend"
);
125
}
126
}
127
135
[[nodiscard]]
bool
event_on_this_rank
() const noexcept {
return
local_event_; }
136
152
[[nodiscard]]
bool
event_on_any_rank
() {
153
// if final_communications() has already been called, any_event == 0 if no event has occurred, otherwise it is 1
154
if
(finalized_)
return
any_event_;
155
156
// if a local event has occurred, we return true
157
if
(local_event_)
return
true
;
158
159
// on the root process, we first check the status of all non-root processes, perform the necessary broadcasts and
160
// return true if an event has occurred
161
if
(comm_.rank() == 0) {
162
root_check_nodes_and_bcast();
163
return
any_event_;
164
}
165
166
// on non-root processes, we check the status of the corresponding broadcast and return true if an event has
167
// occurred
168
MPI_Status status;
169
int
has_received = 0;
170
check_mpi_call
(MPI_Test(&req_ibcast_any_, &has_received, &status),
"MPI_Test"
);
171
return
has_received and any_event_;
172
}
173
187
[[nodiscard]]
bool
event_on_all_ranks
() {
188
// if final_communications() has already been called, all_events == 0 if an event has not occurred on every
189
// process, otherwise it is 1
190
if
(finalized_)
return
all_events_;
191
192
// on the root process, we first check the status of all non-root processes, perform the necessary broadcasts and
193
// return true if an event has occurred on all of them
194
if
(comm_.rank() == 0) {
195
root_check_nodes_and_bcast();
196
return
all_events_;
197
}
198
199
// on non-root processes, we check the status of the broadcast and return true if an event has occurred on all
200
// processes
201
MPI_Status status;
202
int
has_received = 0;
203
check_mpi_call
(MPI_Test(&req_ibcast_all_, &has_received, &status),
"MPI_Test"
);
204
return
has_received and all_events_;
205
}
206
215
void
finalize_communications
() {
216
// prevent multiple calls
217
if
(finalized_)
return
;
218
219
if
(comm_.rank() == 0) {
220
// on root process, wait for all non-root processes to finish their MPI_Isend calls
221
while
(root_check_nodes_and_bcast()) {
222
usleep(100);
// 100 us (micro seconds)
223
}
224
// and perform broadcasts in case they have not been done yet
225
if
(not any_event_) {
check_mpi_call
(MPI_Ibcast(&any_event_, 1, MPI_INT, 0, comm_.get(), &req_ibcast_any_),
"MPI_Ibcast"
); }
226
if
(not all_events_) {
check_mpi_call
(MPI_Ibcast(&all_events_, 1, MPI_INT, 0, comm_.get(), &req_ibcast_all_),
"MPI_Ibcast"
); }
227
}
else
{
228
// on non-root processes, perform MPI_Isend call in case it has not been done yet
229
if
(not local_event_) {
check_mpi_call
(MPI_Isend(&local_event_, 1, MPI_INT, 0, comm_.rank(), comm_.get(), &req_isent_),
"MPI_Isend"
); }
230
}
231
232
// all nodes wait for the broadcasts to be completed
233
MPI_Status status_any, status_all;
234
check_mpi_call
(MPI_Wait(&req_ibcast_any_, &status_any),
"MPI_Wait"
);
235
check_mpi_call
(MPI_Wait(&req_ibcast_all_, &status_all),
"MPI_Wait"
);
236
237
// free the communicator
238
comm_.free();
239
finalized_ =
true
;
240
}
241
242
private
:
243
// Root process checks the status of all non-root processes, performs necessary broadcasts and returns a boolean
244
// that is true if at least one non-root process has not performed its MPI_Isend call yet.
245
bool
root_check_nodes_and_bcast() {
246
EXPECTS(!finalized_);
247
EXPECTS(comm_.rank() == 0);
248
bool
any =
false
;
249
bool
all =
true
;
250
bool
finished =
true
;
251
for
(
auto
&[request, rank_event] : root_futures_) {
252
MPI_Status status;
253
int
rank_received = 0;
254
check_mpi_call
(MPI_Test(&request, &rank_received, &status),
"MPI_Test"
);
255
any |= (rank_received and rank_event);
256
all &= (rank_received and rank_event);
257
finished &= rank_received;
258
}
259
if
(not any_event_ and (any or local_event_)) {
260
any_event_ = 1;
261
check_mpi_call
(MPI_Ibcast(&any_event_, 1, MPI_INT, 0, comm_.get(), &req_ibcast_any_),
"MPI_Ibcast"
);
262
}
263
if
(not all_events_ and all and local_event_) {
264
all_events_ = 1;
265
check_mpi_call
(MPI_Ibcast(&all_events_, 1, MPI_INT, 0, comm_.get(), &req_ibcast_all_),
"MPI_Ibcast"
);
266
}
267
return
not finished;
268
}
269
270
private
:
271
// MPI communicator.
272
mpi::communicator comm_;
273
274
// Future objects stored on the root process for local events on non-root processes.
275
std::vector<future> root_futures_;
276
277
// MPI request for the broadcasting done on the root process in case an event has occurred on any rank.
278
MPI_Request req_ibcast_any_{};
279
280
// MPI request for the broadcasting done on the root process in case an event has occurred on all ranks.
281
MPI_Request req_ibcast_all_{};
282
283
// MPI request for the sending done on non-root processes.
284
MPI_Request req_isent_{};
285
286
// Set to 1, if a local event has occurred on this process.
287
int
local_event_ = 0;
288
289
// Set to 1, if an event has occurred on any process.
290
int
any_event_ = 0;
291
292
// Set to 1, if an event has occurred on all processes.
293
int
all_events_ = 0;
294
295
// Set to true, if finalize_communications() has been called.
296
bool
finalized_ =
false
;
297
};
298
299
}
// namespace mpi
mpi::communicator
C++ wrapper around MPI_Comm providing various convenience functions.
Definition
communicator.hpp:51
mpi::communicator::get
MPI_Comm get() const noexcept
Get the wrapped MPI_Comm object.
Definition
communicator.hpp:63
mpi::monitor::finalize_communications
void finalize_communications()
Finalize all pending communications.
Definition
monitor.hpp:215
mpi::monitor::monitor
monitor(mpi::communicator c)
Construct a monitor on top of a given mpi::communicator.
Definition
monitor.hpp:78
mpi::monitor::event_on_this_rank
bool event_on_this_rank() const noexcept
Check if a local event was reported on this process before finalization.
Definition
monitor.hpp:135
mpi::monitor::event_on_all_ranks
bool event_on_all_ranks()
Check if an event has occurred on all processes.
Definition
monitor.hpp:187
mpi::monitor::~monitor
~monitor()
Destructor calls finalize_communications().
Definition
monitor.hpp:98
mpi::monitor::operator=
monitor & operator=(monitor const &)=delete
Deleted copy assignment operator.
mpi::monitor::report_local_event
void report_local_event()
Report a local event to the root process (rank == 0).
Definition
monitor.hpp:113
mpi::monitor::monitor
monitor(monitor const &)=delete
Deleted copy constructor.
mpi::monitor::event_on_any_rank
bool event_on_any_rank()
Check if an event has occurred on any process.
Definition
monitor.hpp:152
communicator.hpp
Provides a C++ wrapper class for an MPI_Comm object.
mpi::check_mpi_call
void check_mpi_call(int errcode, const std::string &mpi_routine)
Check the success of an MPI call.
Definition
utils.hpp:48
macros.hpp
Macros used in the mpi library.
utils.hpp
Provides general utilities related to MPI.
mpi
monitor.hpp
Generated by
1.18.0