TRIQS/mpi 2.0.0
C++ interface to MPI
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
33namespace 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
99
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
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
C++ wrapper around MPI_Comm providing various convenience functions.
MPI_Comm get() const noexcept
Get the wrapped MPI_Comm object.
void finalize_communications()
Finalize all pending communications.
Definition monitor.hpp:215
monitor(mpi::communicator c)
Construct a monitor on top of a given mpi::communicator.
Definition monitor.hpp:78
bool event_on_this_rank() const noexcept
Check if a local event was reported on this process before finalization.
Definition monitor.hpp:135
bool event_on_all_ranks()
Check if an event has occurred on all processes.
Definition monitor.hpp:187
~monitor()
Destructor calls finalize_communications().
Definition monitor.hpp:98
monitor & operator=(monitor const &)=delete
Deleted copy assignment operator.
void report_local_event()
Report a local event to the root process (rank == 0).
Definition monitor.hpp:113
monitor(monitor const &)=delete
Deleted copy constructor.
bool event_on_any_rank()
Check if an event has occurred on any process.
Definition monitor.hpp:152
Provides a C++ wrapper class for an MPI_Comm object.
void check_mpi_call(int errcode, const std::string &mpi_routine)
Check the success of an MPI call.
Definition utils.hpp:48
Macros used in the mpi library.
Provides general utilities related to MPI.