1 //===-- JSON serialization routines ---------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
29 namespace libc_benchmarks
{
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");
39 return Error::success();
41 return createStringError(errc::io_error
, "Can't parse Integer");
44 static Error
fromJson(const json::Value
&V
, bool &Out
) {
45 if (auto B
= V
.getAsBoolean()) {
47 return Error::success();
49 return createStringError(errc::io_error
, "Can't parse Boolean");
52 static Error
fromJson(const json::Value
&V
, double &Out
) {
53 if (auto S
= V
.getAsNumber()) {
55 return Error::success();
57 return createStringError(errc::io_error
, "Can't parse Double");
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();
65 return createStringError(errc::io_error
, "Can't parse String");
68 static Error
fromJson(const json::Value
&V
, uint32_t &Out
) {
69 return intFromJsonTemplate(V
, Out
);
72 static Error
fromJson(const json::Value
&V
, int &Out
) {
73 return intFromJsonTemplate(V
, Out
);
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();
83 static Error
fromJson(const json::Value
&V
, MaybeAlign
&Out
) {
84 const auto MaybeInt
= V
.getAsInteger();
86 return createStringError(errc::io_error
,
87 "Can't parse Align, not an Integer");
88 const int64_t Value
= *MaybeInt
;
91 return Error::success();
93 if (isPowerOf2_64(Value
)) {
95 return Error::success();
97 return createStringError(errc::io_error
,
98 "Can't parse Align, not a power of two");
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();
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
);
114 return createStringError(errc::io_error
,
115 Twine("Can't parse BenchmarkLog, invalid value '")
119 return Error::success();
122 template <typename C
>
123 Error
vectorFromJsonTemplate(const json::Value
&V
, C
&Out
) {
124 auto *A
= V
.getAsArray();
126 return createStringError(errc::io_error
, "Can't parse Array");
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
)))
132 return Error::success();
135 template <typename T
>
136 static Error
fromJson(const json::Value
&V
, std::vector
<T
> &Out
) {
137 return vectorFromJsonTemplate(V
, Out
);
140 // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
141 class JsonObjectMapper
{
142 const json::Object
*O
;
144 SmallDenseSet
<StringRef
> SeenFields
;
147 explicit JsonObjectMapper(const json::Value
&V
)
148 : O(V
.getAsObject()),
149 E(O
? Error::success()
150 : createStringError(errc::io_error
, "Expected JSON Object")) {}
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
));
164 template <typename T
> void map(StringRef Key
, T
&Out
) {
167 if (const json::Value
*Value
= O
->get(Key
)) {
168 SeenFields
.insert(Key
);
169 E
= fromJson(*Value
, 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();
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();
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();
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();
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();
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();
237 static double seconds(const Duration
&D
) {
238 return std::chrono::duration
<double>(D
).count();
241 Expected
<Study
> parseJsonStudy(StringRef Content
) {
242 Expected
<json::Value
> EV
= json::parse(Content
);
244 return EV
.takeError();
246 if (Error E
= fromJson(*EV
, S
))
251 static StringRef
serialize(const BenchmarkLog
&L
) {
253 case BenchmarkLog::None
:
255 case BenchmarkLog::Last
:
257 case BenchmarkLog::Full
:
260 llvm_unreachable("Unhandled BenchmarkLog value");
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
));
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
);
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
);
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
); });
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
); });
310 void serializeToJson(const Study
&S
, json::OStream
&JOS
) {
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
));
325 } // namespace libc_benchmarks