Loading...
Searching...
No Matches
RgpotGroupMpi.cpp
Go to the documentation of this file.
1// The MPI half of RgpotPot's calculator groups: the construction agreement
2// before the calculator split and the grouped exit handler. It is a library
3// of its own so that librgpot_pot, and with it every eonclient, carries no
4// MPI dependency; RGPotEngine loads it with dlopen only when the process was
5// started under an MPI launcher. rgpot loads its own MPI side the same way.
7
8#include <mpi.h>
9
10#include <algorithm>
11#include <cstddef>
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15#include <vector>
16
17#include "rgpot/CalculatorGroup.hpp"
18
19// _Exit skips the counter flush. A coverage build writes the counters first.
20#ifdef EON_GCOV_DUMP
21extern "C" void __gcov_dump(void);
22#endif
23
24namespace {
25
26// Every rank calls this. A rank that failed locally still enters, so nobody
27// is left in MPI_Comm_split. The shared message is the lowest failing rank's
28// text. Returns 1 when every rank succeeded.
29int agree(const char *local, char *shared, std::size_t cap) {
30 int inited = 0;
31 MPI_Initialized(&inited);
32 if (!inited)
33 MPI_Init(nullptr, nullptr);
34 ::rgpot::finalizeMpiAtExit();
35 const std::size_t localLen = local == nullptr ? 0 : std::strlen(local);
36 const int ok = localLen == 0 ? 1 : 0;
37 int all_ok = 0;
38 MPI_Allreduce(&ok, &all_ok, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
39 if (cap > 0)
40 shared[0] = '\0';
41 if (all_ok)
42 return 1;
43 int rank = 0;
44 int size = 1;
45 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
46 MPI_Comm_size(MPI_COMM_WORLD, &size);
47 const int mine = ok ? size : rank;
48 int owner = 0;
49 MPI_Allreduce(&mine, &owner, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
50 int len = 0;
51 if (rank == owner)
52 len = static_cast<int>(std::min(localLen, std::size_t{4095}));
53 MPI_Bcast(&len, 1, MPI_INT, owner, MPI_COMM_WORLD);
54 std::vector<char> buf(static_cast<std::size_t>(len) + 1, '\0');
55 if (rank == owner && len > 0)
56 std::memcpy(buf.data(), local, static_cast<std::size_t>(len));
57 if (len > 0)
58 MPI_Bcast(buf.data(), len, MPI_CHAR, owner, MPI_COMM_WORLD);
59 if (cap > 0) {
60 const std::size_t n = std::min(static_cast<std::size_t>(len), cap - 1);
61 std::memcpy(shared, buf.data(), n);
62 shared[n] = '\0';
63 }
64 return 0;
65}
66
67// Runs before rgpot's atexit handler and ends in _Exit, so rgpot's handler
68// never runs. It honours rgpot's abort request itself: after a failed
69// engine call the peers can sit in a collective, and MPI_Finalize would
70// wait for them until the walltime kill.
71void hardExit(int status, void *) {
72#ifdef EON_GCOV_DUMP
73 __gcov_dump();
74#endif
75 int inited = 0;
76 int finalized = 0;
77 MPI_Initialized(&inited);
78 MPI_Finalized(&finalized);
79 if (inited && !finalized) {
80 if (::rgpot::mpiAbortRequested()) {
81 std::fflush(nullptr);
82 MPI_Abort(MPI_COMM_WORLD, status != 0 ? status : 1);
83 }
84 MPI_Finalize();
85 }
86 std::fflush(nullptr);
87 std::_Exit(status);
88}
89
90const EonRgpotGroupMpi kApi{EON_RGPOT_GROUP_MPI_VERSION, agree, hardExit};
91
92} // namespace
93
94extern "C" const EonRgpotGroupMpi *eon_rgpot_group_mpi_v1() { return &kApi; }
const EonRgpotGroupMpi * eon_rgpot_group_mpi_v1()
#define EON_RGPOT_GROUP_MPI_VERSION
Function table of librgpot_pot_mpi, the MPI half of RgpotPot's calculator groups.