1 // Copyright 2015 Google Inc. All rights reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <iomanip> // for setprecision
25 #include "benchmark/benchmark.h"
26 #include "complexity.h"
27 #include "string_util.h"
33 std::string
StrEscape(const std::string
& s
) {
35 tmp
.reserve(s
.size());
67 std::string
FormatKV(std::string
const& key
, std::string
const& value
) {
68 return StrFormat("\"%s\": \"%s\"", StrEscape(key
).c_str(),
69 StrEscape(value
).c_str());
72 std::string
FormatKV(std::string
const& key
, const char* value
) {
73 return StrFormat("\"%s\": \"%s\"", StrEscape(key
).c_str(),
74 StrEscape(value
).c_str());
77 std::string
FormatKV(std::string
const& key
, bool value
) {
78 return StrFormat("\"%s\": %s", StrEscape(key
).c_str(),
79 value
? "true" : "false");
82 std::string
FormatKV(std::string
const& key
, int64_t value
) {
84 ss
<< '"' << StrEscape(key
) << "\": " << value
;
88 std::string
FormatKV(std::string
const& key
, double value
) {
90 ss
<< '"' << StrEscape(key
) << "\": ";
92 if (std::isnan(value
))
93 ss
<< (value
< 0 ? "-" : "") << "NaN";
94 else if (std::isinf(value
))
95 ss
<< (value
< 0 ? "-" : "") << "Infinity";
97 const auto max_digits10
=
98 std::numeric_limits
<decltype(value
)>::max_digits10
;
99 const auto max_fractional_digits10
= max_digits10
- 1;
100 ss
<< std::scientific
<< std::setprecision(max_fractional_digits10
)
106 int64_t RoundDouble(double v
) { return std::lround(v
); }
110 bool JSONReporter::ReportContext(const Context
& context
) {
111 std::ostream
& out
= GetOutputStream();
114 std::string
inner_indent(2, ' ');
116 // Open context block and print context information.
117 out
<< inner_indent
<< "\"context\": {\n";
118 std::string
indent(4, ' ');
120 std::string walltime_value
= LocalDateTimeString();
121 out
<< indent
<< FormatKV("date", walltime_value
) << ",\n";
123 out
<< indent
<< FormatKV("host_name", context
.sys_info
.name
) << ",\n";
125 if (Context::executable_name
) {
126 out
<< indent
<< FormatKV("executable", Context::executable_name
) << ",\n";
129 CPUInfo
const& info
= context
.cpu_info
;
130 out
<< indent
<< FormatKV("num_cpus", static_cast<int64_t>(info
.num_cpus
))
133 << FormatKV("mhz_per_cpu",
134 RoundDouble(info
.cycles_per_second
/ 1000000.0))
136 if (CPUInfo::Scaling::UNKNOWN
!= info
.scaling
) {
138 << FormatKV("cpu_scaling_enabled",
139 info
.scaling
== CPUInfo::Scaling::ENABLED
? true : false)
143 out
<< indent
<< "\"caches\": [\n";
144 indent
= std::string(6, ' ');
145 std::string
cache_indent(8, ' ');
146 for (size_t i
= 0; i
< info
.caches
.size(); ++i
) {
147 auto& CI
= info
.caches
[i
];
148 out
<< indent
<< "{\n";
149 out
<< cache_indent
<< FormatKV("type", CI
.type
) << ",\n";
150 out
<< cache_indent
<< FormatKV("level", static_cast<int64_t>(CI
.level
))
152 out
<< cache_indent
<< FormatKV("size", static_cast<int64_t>(CI
.size
))
155 << FormatKV("num_sharing", static_cast<int64_t>(CI
.num_sharing
))
157 out
<< indent
<< "}";
158 if (i
!= info
.caches
.size() - 1) out
<< ",";
161 indent
= std::string(4, ' ');
162 out
<< indent
<< "],\n";
163 out
<< indent
<< "\"load_avg\": [";
164 for (auto it
= info
.load_avg
.begin(); it
!= info
.load_avg
.end();) {
166 if (it
!= info
.load_avg
.end()) out
<< ",";
170 out
<< indent
<< FormatKV("library_version", GetBenchmarkVersion());
174 const char build_type
[] = "release";
176 const char build_type
[] = "debug";
178 out
<< indent
<< FormatKV("library_build_type", build_type
);
181 // NOTE: our json schema is not strictly tied to the library version!
182 out
<< indent
<< FormatKV("json_schema_version", int64_t(1));
184 std::map
<std::string
, std::string
>* global_context
=
185 internal::GetGlobalContext();
187 if (global_context
!= nullptr) {
188 for (const auto& kv
: *global_context
) {
190 out
<< indent
<< FormatKV(kv
.first
, kv
.second
);
195 // Close context block and open the list of benchmarks.
196 out
<< inner_indent
<< "},\n";
197 out
<< inner_indent
<< "\"benchmarks\": [\n";
201 void JSONReporter::ReportRuns(std::vector
<Run
> const& reports
) {
202 if (reports
.empty()) {
205 std::string
indent(4, ' ');
206 std::ostream
& out
= GetOutputStream();
207 if (!first_report_
) {
210 first_report_
= false;
212 for (auto it
= reports
.begin(); it
!= reports
.end(); ++it
) {
213 out
<< indent
<< "{\n";
215 out
<< indent
<< '}';
217 if (++it_cp
!= reports
.end()) {
223 void JSONReporter::Finalize() {
224 // Close the list of benchmarks and the top level object.
225 GetOutputStream() << "\n ]\n}\n";
228 void JSONReporter::PrintRunData(Run
const& run
) {
229 std::string
indent(6, ' ');
230 std::ostream
& out
= GetOutputStream();
231 out
<< indent
<< FormatKV("name", run
.benchmark_name()) << ",\n";
232 out
<< indent
<< FormatKV("family_index", run
.family_index
) << ",\n";
234 << FormatKV("per_family_instance_index", run
.per_family_instance_index
)
236 out
<< indent
<< FormatKV("run_name", run
.run_name
.str()) << ",\n";
237 out
<< indent
<< FormatKV("run_type", [&run
]() -> const char* {
238 switch (run
.run_type
) {
239 case BenchmarkReporter::Run::RT_Iteration
:
241 case BenchmarkReporter::Run::RT_Aggregate
:
244 BENCHMARK_UNREACHABLE();
246 out
<< indent
<< FormatKV("repetitions", run
.repetitions
) << ",\n";
247 if (run
.run_type
!= BenchmarkReporter::Run::RT_Aggregate
) {
248 out
<< indent
<< FormatKV("repetition_index", run
.repetition_index
)
251 out
<< indent
<< FormatKV("threads", run
.threads
) << ",\n";
252 if (run
.run_type
== BenchmarkReporter::Run::RT_Aggregate
) {
253 out
<< indent
<< FormatKV("aggregate_name", run
.aggregate_name
) << ",\n";
254 out
<< indent
<< FormatKV("aggregate_unit", [&run
]() -> const char* {
255 switch (run
.aggregate_unit
) {
256 case StatisticUnit::kTime
:
258 case StatisticUnit::kPercentage
:
261 BENCHMARK_UNREACHABLE();
264 if (internal::SkippedWithError
== run
.skipped
) {
265 out
<< indent
<< FormatKV("error_occurred", true) << ",\n";
266 out
<< indent
<< FormatKV("error_message", run
.skip_message
) << ",\n";
267 } else if (internal::SkippedWithMessage
== run
.skipped
) {
268 out
<< indent
<< FormatKV("skipped", true) << ",\n";
269 out
<< indent
<< FormatKV("skip_message", run
.skip_message
) << ",\n";
271 if (!run
.report_big_o
&& !run
.report_rms
) {
272 out
<< indent
<< FormatKV("iterations", run
.iterations
) << ",\n";
273 if (run
.run_type
!= Run::RT_Aggregate
||
274 run
.aggregate_unit
== StatisticUnit::kTime
) {
275 out
<< indent
<< FormatKV("real_time", run
.GetAdjustedRealTime())
277 out
<< indent
<< FormatKV("cpu_time", run
.GetAdjustedCPUTime());
279 assert(run
.aggregate_unit
== StatisticUnit::kPercentage
);
280 out
<< indent
<< FormatKV("real_time", run
.real_accumulated_time
)
282 out
<< indent
<< FormatKV("cpu_time", run
.cpu_accumulated_time
);
285 << indent
<< FormatKV("time_unit", GetTimeUnitString(run
.time_unit
));
286 } else if (run
.report_big_o
) {
287 out
<< indent
<< FormatKV("cpu_coefficient", run
.GetAdjustedCPUTime())
289 out
<< indent
<< FormatKV("real_coefficient", run
.GetAdjustedRealTime())
291 out
<< indent
<< FormatKV("big_o", GetBigOString(run
.complexity
)) << ",\n";
292 out
<< indent
<< FormatKV("time_unit", GetTimeUnitString(run
.time_unit
));
293 } else if (run
.report_rms
) {
294 out
<< indent
<< FormatKV("rms", run
.GetAdjustedCPUTime());
297 for (auto& c
: run
.counters
) {
298 out
<< ",\n" << indent
<< FormatKV(c
.first
, c
.second
);
301 if (run
.memory_result
) {
302 const MemoryManager::Result memory_result
= *run
.memory_result
;
303 out
<< ",\n" << indent
<< FormatKV("allocs_per_iter", run
.allocs_per_iter
);
305 << indent
<< FormatKV("max_bytes_used", memory_result
.max_bytes_used
);
307 auto report_if_present
= [&out
, &indent
](const std::string
& label
,
309 if (val
!= MemoryManager::TombstoneValue
)
310 out
<< ",\n" << indent
<< FormatKV(label
, val
);
313 report_if_present("total_allocated_bytes",
314 memory_result
.total_allocated_bytes
);
315 report_if_present("net_heap_growth", memory_result
.net_heap_growth
);
318 if (!run
.report_label
.empty()) {
319 out
<< ",\n" << indent
<< FormatKV("label", run
.report_label
);
324 const int64_t MemoryManager::TombstoneValue
=
325 std::numeric_limits
<int64_t>::max();
327 } // end namespace benchmark