xref: /llvm-project/libc/benchmarks/JSON.cpp (revision 660c33e51de20b5a414b029a6dbae8a33aefabd4)
1 //===-- JSON serialization routines ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "JSON.h"
10 #include "LibcBenchmark.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/SmallVector.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/ADT/StringSwitch.h"
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/ErrorHandling.h"
18 #include "llvm/Support/JSON.h"
19 #include "llvm/Support/MathExtras.h"
20 
21 #include <chrono>
22 #include <limits>
23 #include <memory>
24 #include <optional>
25 #include <string>
26 #include <vector>
27 
28 namespace llvm {
29 namespace libc_benchmarks {
30 
31 template <typename T>
intFromJsonTemplate(const json::Value & V,T & Out)32 static Error intFromJsonTemplate(const json::Value &V, T &Out) {
33   if (const auto &MaybeInt64 = V.getAsInteger()) {
34     int64_t Value = *MaybeInt64;
35     if (Value < std::numeric_limits<T>::min() ||
36         Value > std::numeric_limits<T>::max())
37       return createStringError(errc::io_error, "Out of bound Integer");
38     Out = Value;
39     return Error::success();
40   }
41   return createStringError(errc::io_error, "Can't parse Integer");
42 }
43 
fromJson(const json::Value & V,bool & Out)44 static Error fromJson(const json::Value &V, bool &Out) {
45   if (auto B = V.getAsBoolean()) {
46     Out = *B;
47     return Error::success();
48   }
49   return createStringError(errc::io_error, "Can't parse Boolean");
50 }
51 
fromJson(const json::Value & V,double & Out)52 static Error fromJson(const json::Value &V, double &Out) {
53   if (auto S = V.getAsNumber()) {
54     Out = *S;
55     return Error::success();
56   }
57   return createStringError(errc::io_error, "Can't parse Double");
58 }
59 
fromJson(const json::Value & V,std::string & Out)60 static Error fromJson(const json::Value &V, std::string &Out) {
61   if (auto S = V.getAsString()) {
62     Out = std::string(*S);
63     return Error::success();
64   }
65   return createStringError(errc::io_error, "Can't parse String");
66 }
67 
fromJson(const json::Value & V,uint32_t & Out)68 static Error fromJson(const json::Value &V, uint32_t &Out) {
69   return intFromJsonTemplate(V, Out);
70 }
71 
fromJson(const json::Value & V,int & Out)72 static Error fromJson(const json::Value &V, int &Out) {
73   return intFromJsonTemplate(V, Out);
74 }
75 
fromJson(const json::Value & V,libc_benchmarks::Duration & D)76 static Error fromJson(const json::Value &V, libc_benchmarks::Duration &D) {
77   if (V.kind() != json::Value::Kind::Number)
78     return createStringError(errc::io_error, "Can't parse Duration");
79   D = libc_benchmarks::Duration(*V.getAsNumber());
80   return Error::success();
81 }
82 
fromJson(const json::Value & V,MaybeAlign & Out)83 static Error fromJson(const json::Value &V, MaybeAlign &Out) {
84   const auto MaybeInt = V.getAsInteger();
85   if (!MaybeInt)
86     return createStringError(errc::io_error,
87                              "Can't parse Align, not an Integer");
88   const int64_t Value = *MaybeInt;
89   if (!Value) {
90     Out = std::nullopt;
91     return Error::success();
92   }
93   if (isPowerOf2_64(Value)) {
94     Out = Align(Value);
95     return Error::success();
96   }
97   return createStringError(errc::io_error,
98                            "Can't parse Align, not a power of two");
99 }
100 
fromJson(const json::Value & V,libc_benchmarks::BenchmarkLog & Out)101 static Error fromJson(const json::Value &V,
102                       libc_benchmarks::BenchmarkLog &Out) {
103   if (V.kind() != json::Value::Kind::String)
104     return createStringError(errc::io_error,
105                              "Can't parse BenchmarkLog, not a String");
106   const auto String = *V.getAsString();
107   auto Parsed =
108       llvm::StringSwitch<std::optional<libc_benchmarks::BenchmarkLog>>(String)
109           .Case("None", libc_benchmarks::BenchmarkLog::None)
110           .Case("Last", libc_benchmarks::BenchmarkLog::Last)
111           .Case("Full", libc_benchmarks::BenchmarkLog::Full)
112           .Default(std::nullopt);
113   if (!Parsed)
114     return createStringError(errc::io_error,
115                              Twine("Can't parse BenchmarkLog, invalid value '")
116                                  .concat(String)
117                                  .concat("'"));
118   Out = *Parsed;
119   return Error::success();
120 }
121 
122 template <typename C>
vectorFromJsonTemplate(const json::Value & V,C & Out)123 Error vectorFromJsonTemplate(const json::Value &V, C &Out) {
124   auto *A = V.getAsArray();
125   if (!A)
126     return createStringError(errc::io_error, "Can't parse Array");
127   Out.clear();
128   Out.resize(A->size());
129   for (auto InOutPair : llvm::zip(*A, Out))
130     if (auto E = fromJson(std::get<0>(InOutPair), std::get<1>(InOutPair)))
131       return std::move(E);
132   return Error::success();
133 }
134 
135 template <typename T>
fromJson(const json::Value & V,std::vector<T> & Out)136 static Error fromJson(const json::Value &V, std::vector<T> &Out) {
137   return vectorFromJsonTemplate(V, Out);
138 }
139 
140 // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
141 class JsonObjectMapper {
142   const json::Object *O;
143   Error E;
144   SmallDenseSet<StringRef> SeenFields;
145 
146 public:
JsonObjectMapper(const json::Value & V)147   explicit JsonObjectMapper(const json::Value &V)
148       : O(V.getAsObject()),
149         E(O ? Error::success()
150             : createStringError(errc::io_error, "Expected JSON Object")) {}
151 
takeError()152   Error takeError() {
153     if (E)
154       return std::move(E);
155     for (const auto &Itr : *O) {
156       const StringRef Key = Itr.getFirst();
157       if (!SeenFields.count(Key))
158         E = createStringError(errc::io_error,
159                               Twine("Unknown field: ").concat(Key));
160     }
161     return std::move(E);
162   }
163 
map(StringRef Key,T & Out)164   template <typename T> void map(StringRef Key, T &Out) {
165     if (E)
166       return;
167     if (const json::Value *Value = O->get(Key)) {
168       SeenFields.insert(Key);
169       E = fromJson(*Value, Out);
170     }
171   }
172 };
173 
fromJson(const json::Value & V,libc_benchmarks::BenchmarkOptions & Out)174 static Error fromJson(const json::Value &V,
175                       libc_benchmarks::BenchmarkOptions &Out) {
176   JsonObjectMapper O(V);
177   O.map("MinDuration", Out.MinDuration);
178   O.map("MaxDuration", Out.MaxDuration);
179   O.map("InitialIterations", Out.InitialIterations);
180   O.map("MaxIterations", Out.MaxIterations);
181   O.map("MinSamples", Out.MinSamples);
182   O.map("MaxSamples", Out.MaxSamples);
183   O.map("Epsilon", Out.Epsilon);
184   O.map("ScalingFactor", Out.ScalingFactor);
185   O.map("Log", Out.Log);
186   return O.takeError();
187 }
188 
fromJson(const json::Value & V,libc_benchmarks::StudyConfiguration & Out)189 static Error fromJson(const json::Value &V,
190                       libc_benchmarks::StudyConfiguration &Out) {
191   JsonObjectMapper O(V);
192   O.map("Function", Out.Function);
193   O.map("NumTrials", Out.NumTrials);
194   O.map("IsSweepMode", Out.IsSweepMode);
195   O.map("SweepModeMaxSize", Out.SweepModeMaxSize);
196   O.map("SizeDistributionName", Out.SizeDistributionName);
197   O.map("AccessAlignment", Out.AccessAlignment);
198   O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt);
199   return O.takeError();
200 }
201 
fromJson(const json::Value & V,libc_benchmarks::CacheInfo & Out)202 static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
203   JsonObjectMapper O(V);
204   O.map("Type", Out.Type);
205   O.map("Level", Out.Level);
206   O.map("Size", Out.Size);
207   O.map("NumSharing", Out.NumSharing);
208   return O.takeError();
209 }
210 
fromJson(const json::Value & V,libc_benchmarks::HostState & Out)211 static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
212   JsonObjectMapper O(V);
213   O.map("CpuName", Out.CpuName);
214   O.map("CpuFrequency", Out.CpuFrequency);
215   O.map("Caches", Out.Caches);
216   return O.takeError();
217 }
218 
fromJson(const json::Value & V,libc_benchmarks::Runtime & Out)219 static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {
220   JsonObjectMapper O(V);
221   O.map("Host", Out.Host);
222   O.map("BufferSize", Out.BufferSize);
223   O.map("BatchParameterCount", Out.BatchParameterCount);
224   O.map("BenchmarkOptions", Out.BenchmarkOptions);
225   return O.takeError();
226 }
227 
fromJson(const json::Value & V,libc_benchmarks::Study & Out)228 static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
229   JsonObjectMapper O(V);
230   O.map("StudyName", Out.StudyName);
231   O.map("Runtime", Out.Runtime);
232   O.map("Configuration", Out.Configuration);
233   O.map("Measurements", Out.Measurements);
234   return O.takeError();
235 }
236 
seconds(const Duration & D)237 static double seconds(const Duration &D) {
238   return std::chrono::duration<double>(D).count();
239 }
240 
parseJsonStudy(StringRef Content)241 Expected<Study> parseJsonStudy(StringRef Content) {
242   Expected<json::Value> EV = json::parse(Content);
243   if (!EV)
244     return EV.takeError();
245   Study S;
246   if (Error E = fromJson(*EV, S))
247     return std::move(E);
248   return S;
249 }
250 
serialize(const BenchmarkLog & L)251 static StringRef serialize(const BenchmarkLog &L) {
252   switch (L) {
253   case BenchmarkLog::None:
254     return "None";
255   case BenchmarkLog::Last:
256     return "Last";
257   case BenchmarkLog::Full:
258     return "Full";
259   }
260   llvm_unreachable("Unhandled BenchmarkLog value");
261 }
262 
serialize(const BenchmarkOptions & BO,json::OStream & JOS)263 static void serialize(const BenchmarkOptions &BO, json::OStream &JOS) {
264   JOS.attribute("MinDuration", seconds(BO.MinDuration));
265   JOS.attribute("MaxDuration", seconds(BO.MaxDuration));
266   JOS.attribute("InitialIterations", BO.InitialIterations);
267   JOS.attribute("MaxIterations", BO.MaxIterations);
268   JOS.attribute("MinSamples", BO.MinSamples);
269   JOS.attribute("MaxSamples", BO.MaxSamples);
270   JOS.attribute("Epsilon", BO.Epsilon);
271   JOS.attribute("ScalingFactor", BO.ScalingFactor);
272   JOS.attribute("Log", serialize(BO.Log));
273 }
274 
serialize(const CacheInfo & CI,json::OStream & JOS)275 static void serialize(const CacheInfo &CI, json::OStream &JOS) {
276   JOS.attribute("Type", CI.Type);
277   JOS.attribute("Level", CI.Level);
278   JOS.attribute("Size", CI.Size);
279   JOS.attribute("NumSharing", CI.NumSharing);
280 }
281 
serialize(const StudyConfiguration & SC,json::OStream & JOS)282 static void serialize(const StudyConfiguration &SC, json::OStream &JOS) {
283   JOS.attribute("Function", SC.Function);
284   JOS.attribute("NumTrials", SC.NumTrials);
285   JOS.attribute("IsSweepMode", SC.IsSweepMode);
286   JOS.attribute("SweepModeMaxSize", SC.SweepModeMaxSize);
287   JOS.attribute("SizeDistributionName", SC.SizeDistributionName);
288   JOS.attribute("AccessAlignment",
289                 static_cast<int64_t>(SC.AccessAlignment->value()));
290   JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
291 }
292 
serialize(const HostState & HS,json::OStream & JOS)293 static void serialize(const HostState &HS, json::OStream &JOS) {
294   JOS.attribute("CpuName", HS.CpuName);
295   JOS.attribute("CpuFrequency", HS.CpuFrequency);
296   JOS.attributeArray("Caches", [&]() {
297     for (const auto &CI : HS.Caches)
298       JOS.object([&]() { serialize(CI, JOS); });
299   });
300 }
301 
serialize(const Runtime & RI,json::OStream & JOS)302 static void serialize(const Runtime &RI, json::OStream &JOS) {
303   JOS.attributeObject("Host", [&]() { serialize(RI.Host, JOS); });
304   JOS.attribute("BufferSize", RI.BufferSize);
305   JOS.attribute("BatchParameterCount", RI.BatchParameterCount);
306   JOS.attributeObject("BenchmarkOptions",
307                       [&]() { serialize(RI.BenchmarkOptions, JOS); });
308 }
309 
serializeToJson(const Study & S,json::OStream & JOS)310 void serializeToJson(const Study &S, json::OStream &JOS) {
311   JOS.object([&]() {
312     JOS.attribute("StudyName", S.StudyName);
313     JOS.attributeObject("Runtime", [&]() { serialize(S.Runtime, JOS); });
314     JOS.attributeObject("Configuration",
315                         [&]() { serialize(S.Configuration, JOS); });
316     if (!S.Measurements.empty()) {
317       JOS.attributeArray("Measurements", [&]() {
318         for (const auto &M : S.Measurements)
319           JOS.value(seconds(M));
320       });
321     }
322   });
323 }
324 
325 } // namespace libc_benchmarks
326 } // namespace llvm
327