Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
RequestWorker.cpp
Go to the documentation of this file.
1// Native request runner. The optional MPI target initializes application MPI
2// and executes identical native calls on every rank. No Python dependency.
4#include <boost/json.hpp>
5#include <cstdlib>
6#include <iostream>
7#include <iterator>
8#include <memory>
9#include <string>
10#ifdef MAESTRO_REQUEST_MPI
11#include <mpi.h>
12#endif
13namespace json = boost::json;
14using Buffer = std::unique_ptr<char, decltype(&FreeResult)>;
15constexpr size_t MaxRequest = 16 * 1024 * 1024;
16
17void Output(const json::object &result, bool framed)
18{
19 if (framed)
20 std::cout << "MAESTRO_RESULT_V2 ";
21 std::cout << json::serialize(result) << '\n' << std::flush;
22}
23
24json::object Decode(char *pointer)
25{
26 Buffer buffer(pointer, FreeResult);
27 if (!buffer)
28 throw std::runtime_error("Native API returned a null buffer");
29 return json::parse(buffer.get()).as_object();
30}
31
32bool Success(const json::object &value)
33{
34 const auto *ok = value.if_contains("ok");
35 return ok && ok->is_bool() && ok->as_bool();
36}
37
38int main(int argc, char **argv)
39{
40 int rank = 0;
41#ifdef MAESTRO_REQUEST_MPI
42 int ranks = 1;
43#endif
44 bool framed = false, capabilities = false, validate = false;
45 for (int i = 1; i < argc; ++i)
46 {
47 const std::string arg(argv[i]);
48 if (arg == "--framed-result")
49 framed = true;
50 else if (arg == "--capabilities")
51 capabilities = true;
52 else if (arg == "--validate")
53 validate = true;
54 else
55 {
56 std::cerr << "Unknown argument: " << arg << '\n';
57 return 2;
58 }
59 }
60#ifdef MAESTRO_REQUEST_MPI
61 int provided = MPI_THREAD_SINGLE;
62 if (MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided) != MPI_SUCCESS)
63 return 3;
64 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
65 MPI_Comm_size(MPI_COMM_WORLD, &ranks);
66 if (provided < MPI_THREAD_FUNNELED)
67 {
68 MPI_Abort(MPI_COMM_WORLD, 3);
69 return 3;
70 }
71#endif
72 try
73 {
74 std::string input;
75 if (!capabilities && rank == 0)
76 {
77 char buffer[8192];
78 while (std::cin.read(buffer, sizeof(buffer)) || std::cin.gcount())
79 {
80 input.append(buffer, static_cast<size_t>(std::cin.gcount()));
81 if (input.size() > MaxRequest)
82 throw std::invalid_argument("Request exceeds 16 MiB");
83 }
84 }
85#ifdef MAESTRO_REQUEST_MPI
86 if (rank == 0 && framed)
87 {
88 const auto *job = std::getenv("SLURM_JOB_ID");
89 const auto *step = std::getenv("SLURM_STEP_ID");
90 if (job && step)
91 std::cout << "MAESTRO_STEP_V2 " << job << '.' << step << '\n' << std::flush;
92 }
93 uint64_t length = input.size();
94 MPI_Bcast(&length, 1, MPI_UINT64_T, 0, MPI_COMM_WORLD);
95 if (length > MaxRequest)
96 throw std::invalid_argument("Oversized collective request");
97 input.resize(length);
98 MPI_Bcast(input.data(), static_cast<int>(length), MPI_BYTE, 0, MPI_COMM_WORLD);
99#endif
100 auto result = capabilities ? Decode(MaestroGetCapabilitiesJson()) : Decode(MaestroValidateRequestJson(input.c_str()));
101 int valid = Success(result) ? 1 : 0;
102#ifdef MAESTRO_REQUEST_MPI
103 int allValid = 0;
104 MPI_Allreduce(&valid, &allValid, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
105 valid = allValid;
106 if (!valid && Success(result))
107 result =
108 json::object{{"ok", false}, {"error", json::object{{"code", "collective_validation_failure"}, {"message", "A peer rejected the request"}}}};
109#endif
110 if (valid && !capabilities && !validate)
111 result = Decode(MaestroRunRequestJson(input.c_str()));
112 if (!Success(result))
113 {
114 if (rank == 0)
115 Output(result, framed);
116 else
117 std::cerr << json::serialize(result) << '\n';
118#ifdef MAESTRO_REQUEST_MPI
119 // A peer may still be inside a backend collective. Abort instead of
120 // waiting in another collective and deadlocking the entire worker group.
121 MPI_Abort(MPI_COMM_WORLD, 1);
122#endif
123 return 1;
124 }
125#ifdef MAESTRO_REQUEST_MPI
126 MPI_Barrier(MPI_COMM_WORLD); // All Run calls destroyed their states.
127 const auto shutdown = Decode(MaestroFinalizeDistributedMpiGpuJson());
128 if (!Success(shutdown))
129 throw std::runtime_error(json::serialize(shutdown));
130 result["mpi"] = json::object{{"ranks", ranks}, {"result_rank", 0}, {"aggregation", "one_replicated_result"}};
131 MPI_Finalize();
132#endif
133 if (rank == 0)
134 Output(result, framed);
135 return 0;
136 }
137 catch (const std::exception &error)
138 {
139 json::object result{{"schema_version", 2}, {"ok", false}, {"error", json::object{{"code", "worker_failure"}, {"message", error.what()}}}};
140 if (rank == 0)
141 Output(result, framed);
142 else
143 std::cerr << json::serialize(result) << '\n';
144#ifdef MAESTRO_REQUEST_MPI
145 int finalized = 0;
146 MPI_Finalized(&finalized);
147 if (!finalized)
148 MPI_Abort(MPI_COMM_WORLD, 1);
149#endif
150 return 1;
151 }
152}
void FreeResult(char *result)
char * MaestroRunRequestJson(const char *request)
char * MaestroValidateRequestJson(const char *request)
char * MaestroFinalizeDistributedMpiGpuJson(void)
char * MaestroGetCapabilitiesJson(void)
bool Success(const json::object &value)
std::unique_ptr< char, decltype(&FreeResult)> Buffer
json::object Decode(char *pointer)
int main(int argc, char **argv)
constexpr size_t MaxRequest
void Output(const json::object &result, bool framed)