1 | // Copyright 2015 Google Inc. All rights reserved. |
2 | // |
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 |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
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. |
14 | |
15 | #include "benchmark/benchmark.h" |
16 | #include "complexity.h" |
17 | |
18 | #include <algorithm> |
19 | #include <cmath> |
20 | #include <cstdint> |
21 | #include <iomanip> // for setprecision |
22 | #include <iostream> |
23 | #include <limits> |
24 | #include <string> |
25 | #include <tuple> |
26 | #include <vector> |
27 | |
28 | #include "string_util.h" |
29 | #include "timers.h" |
30 | |
31 | namespace benchmark { |
32 | namespace internal { |
33 | extern std::map<std::string, std::string>* global_context; |
34 | } |
35 | |
36 | namespace { |
37 | |
38 | std::string StrEscape(const std::string & s) { |
39 | std::string tmp; |
40 | tmp.reserve(s.size()); |
41 | for (char c : s) { |
42 | switch (c) { |
43 | case '\b': tmp += "\\b" ; break; |
44 | case '\f': tmp += "\\f" ; break; |
45 | case '\n': tmp += "\\n" ; break; |
46 | case '\r': tmp += "\\r" ; break; |
47 | case '\t': tmp += "\\t" ; break; |
48 | case '\\': tmp += "\\\\" ; break; |
49 | case '"' : tmp += "\\\"" ; break; |
50 | default : tmp += c; break; |
51 | } |
52 | } |
53 | return tmp; |
54 | } |
55 | |
56 | std::string FormatKV(std::string const& key, std::string const& value) { |
57 | return StrFormat("\"%s\": \"%s\"" , StrEscape(key).c_str(), StrEscape(value).c_str()); |
58 | } |
59 | |
60 | std::string FormatKV(std::string const& key, const char* value) { |
61 | return StrFormat("\"%s\": \"%s\"" , StrEscape(key).c_str(), StrEscape(value).c_str()); |
62 | } |
63 | |
64 | std::string FormatKV(std::string const& key, bool value) { |
65 | return StrFormat("\"%s\": %s" , StrEscape(key).c_str(), value ? "true" : "false" ); |
66 | } |
67 | |
68 | std::string FormatKV(std::string const& key, int64_t value) { |
69 | std::stringstream ss; |
70 | ss << '"' << StrEscape(key) << "\": " << value; |
71 | return ss.str(); |
72 | } |
73 | |
74 | std::string FormatKV(std::string const& key, IterationCount value) { |
75 | std::stringstream ss; |
76 | ss << '"' << StrEscape(key) << "\": " << value; |
77 | return ss.str(); |
78 | } |
79 | |
80 | std::string FormatKV(std::string const& key, double value) { |
81 | std::stringstream ss; |
82 | ss << '"' << StrEscape(key) << "\": " ; |
83 | |
84 | if (std::isnan(value)) |
85 | ss << (value < 0 ? "-" : "" ) << "NaN" ; |
86 | else if (std::isinf(value)) |
87 | ss << (value < 0 ? "-" : "" ) << "Infinity" ; |
88 | else { |
89 | const auto max_digits10 = |
90 | std::numeric_limits<decltype(value)>::max_digits10; |
91 | const auto max_fractional_digits10 = max_digits10 - 1; |
92 | ss << std::scientific << std::setprecision(max_fractional_digits10) |
93 | << value; |
94 | } |
95 | return ss.str(); |
96 | } |
97 | |
98 | int64_t RoundDouble(double v) { return std::lround(v); } |
99 | |
100 | } // end namespace |
101 | |
102 | bool JSONReporter::ReportContext(const Context& context) { |
103 | std::ostream& out = GetOutputStream(); |
104 | |
105 | out << "{\n" ; |
106 | std::string inner_indent(2, ' '); |
107 | |
108 | // Open context block and print context information. |
109 | out << inner_indent << "\"context\": {\n" ; |
110 | std::string indent(4, ' '); |
111 | |
112 | std::string walltime_value = LocalDateTimeString(); |
113 | out << indent << FormatKV("date" , walltime_value) << ",\n" ; |
114 | |
115 | out << indent << FormatKV("host_name" , context.sys_info.name) << ",\n" ; |
116 | |
117 | if (Context::executable_name) { |
118 | out << indent << FormatKV("executable" , Context::executable_name) << ",\n" ; |
119 | } |
120 | |
121 | CPUInfo const& info = context.cpu_info; |
122 | out << indent << FormatKV("num_cpus" , static_cast<int64_t>(info.num_cpus)) |
123 | << ",\n" ; |
124 | out << indent |
125 | << FormatKV("mhz_per_cpu" , |
126 | RoundDouble(info.cycles_per_second / 1000000.0)) |
127 | << ",\n" ; |
128 | if (CPUInfo::Scaling::UNKNOWN != info.scaling) { |
129 | out << indent << FormatKV("cpu_scaling_enabled" , info.scaling == CPUInfo::Scaling::ENABLED ? true : false) |
130 | << ",\n" ; |
131 | } |
132 | |
133 | out << indent << "\"caches\": [\n" ; |
134 | indent = std::string(6, ' '); |
135 | std::string cache_indent(8, ' '); |
136 | for (size_t i = 0; i < info.caches.size(); ++i) { |
137 | auto& CI = info.caches[i]; |
138 | out << indent << "{\n" ; |
139 | out << cache_indent << FormatKV("type" , CI.type) << ",\n" ; |
140 | out << cache_indent << FormatKV("level" , static_cast<int64_t>(CI.level)) |
141 | << ",\n" ; |
142 | out << cache_indent |
143 | << FormatKV("size" , static_cast<int64_t>(CI.size)) << ",\n" ; |
144 | out << cache_indent |
145 | << FormatKV("num_sharing" , static_cast<int64_t>(CI.num_sharing)) |
146 | << "\n" ; |
147 | out << indent << "}" ; |
148 | if (i != info.caches.size() - 1) out << "," ; |
149 | out << "\n" ; |
150 | } |
151 | indent = std::string(4, ' '); |
152 | out << indent << "],\n" ; |
153 | out << indent << "\"load_avg\": [" ; |
154 | for (auto it = info.load_avg.begin(); it != info.load_avg.end();) { |
155 | out << *it++; |
156 | if (it != info.load_avg.end()) out << "," ; |
157 | } |
158 | out << "],\n" ; |
159 | |
160 | #if defined(NDEBUG) |
161 | const char build_type[] = "release" ; |
162 | #else |
163 | const char build_type[] = "debug" ; |
164 | #endif |
165 | out << indent << FormatKV("library_build_type" , build_type) << "\n" ; |
166 | |
167 | if (internal::global_context != nullptr) { |
168 | for (const auto& kv: *internal::global_context) { |
169 | out << indent << FormatKV(kv.first, kv.second) << "\n" ; |
170 | } |
171 | } |
172 | |
173 | // Close context block and open the list of benchmarks. |
174 | out << inner_indent << "},\n" ; |
175 | out << inner_indent << "\"benchmarks\": [\n" ; |
176 | return true; |
177 | } |
178 | |
179 | void JSONReporter::ReportRuns(std::vector<Run> const& reports) { |
180 | if (reports.empty()) { |
181 | return; |
182 | } |
183 | std::string indent(4, ' '); |
184 | std::ostream& out = GetOutputStream(); |
185 | if (!first_report_) { |
186 | out << ",\n" ; |
187 | } |
188 | first_report_ = false; |
189 | |
190 | for (auto it = reports.begin(); it != reports.end(); ++it) { |
191 | out << indent << "{\n" ; |
192 | PrintRunData(*it); |
193 | out << indent << '}'; |
194 | auto it_cp = it; |
195 | if (++it_cp != reports.end()) { |
196 | out << ",\n" ; |
197 | } |
198 | } |
199 | } |
200 | |
201 | void JSONReporter::Finalize() { |
202 | // Close the list of benchmarks and the top level object. |
203 | GetOutputStream() << "\n ]\n}\n" ; |
204 | } |
205 | |
206 | void JSONReporter::PrintRunData(Run const& run) { |
207 | std::string indent(6, ' '); |
208 | std::ostream& out = GetOutputStream(); |
209 | out << indent << FormatKV("name" , run.benchmark_name()) << ",\n" ; |
210 | out << indent << FormatKV("run_name" , run.run_name.str()) << ",\n" ; |
211 | out << indent << FormatKV("run_type" , [&run]() -> const char* { |
212 | switch (run.run_type) { |
213 | case BenchmarkReporter::Run::RT_Iteration: |
214 | return "iteration" ; |
215 | case BenchmarkReporter::Run::RT_Aggregate: |
216 | return "aggregate" ; |
217 | } |
218 | BENCHMARK_UNREACHABLE(); |
219 | }()) << ",\n" ; |
220 | out << indent << FormatKV("repetitions" , run.repetitions) << ",\n" ; |
221 | if (run.run_type != BenchmarkReporter::Run::RT_Aggregate) { |
222 | out << indent << FormatKV("repetition_index" , run.repetition_index) |
223 | << ",\n" ; |
224 | } |
225 | out << indent << FormatKV("threads" , run.threads) << ",\n" ; |
226 | if (run.run_type == BenchmarkReporter::Run::RT_Aggregate) { |
227 | out << indent << FormatKV("aggregate_name" , run.aggregate_name) << ",\n" ; |
228 | } |
229 | if (run.error_occurred) { |
230 | out << indent << FormatKV("error_occurred" , run.error_occurred) << ",\n" ; |
231 | out << indent << FormatKV("error_message" , run.error_message) << ",\n" ; |
232 | } |
233 | if (!run.report_big_o && !run.report_rms) { |
234 | out << indent << FormatKV("iterations" , run.iterations) << ",\n" ; |
235 | out << indent << FormatKV("real_time" , run.GetAdjustedRealTime()) << ",\n" ; |
236 | out << indent << FormatKV("cpu_time" , run.GetAdjustedCPUTime()); |
237 | out << ",\n" |
238 | << indent << FormatKV("time_unit" , GetTimeUnitString(run.time_unit)); |
239 | } else if (run.report_big_o) { |
240 | out << indent << FormatKV("cpu_coefficient" , run.GetAdjustedCPUTime()) |
241 | << ",\n" ; |
242 | out << indent << FormatKV("real_coefficient" , run.GetAdjustedRealTime()) |
243 | << ",\n" ; |
244 | out << indent << FormatKV("big_o" , GetBigOString(run.complexity)) << ",\n" ; |
245 | out << indent << FormatKV("time_unit" , GetTimeUnitString(run.time_unit)); |
246 | } else if (run.report_rms) { |
247 | out << indent << FormatKV("rms" , run.GetAdjustedCPUTime()); |
248 | } |
249 | |
250 | for (auto& c : run.counters) { |
251 | out << ",\n" << indent << FormatKV(c.first, c.second); |
252 | } |
253 | |
254 | if (run.has_memory_result) { |
255 | out << ",\n" << indent << FormatKV("allocs_per_iter" , run.allocs_per_iter); |
256 | out << ",\n" << indent << FormatKV("max_bytes_used" , run.max_bytes_used); |
257 | } |
258 | |
259 | if (!run.report_label.empty()) { |
260 | out << ",\n" << indent << FormatKV("label" , run.report_label); |
261 | } |
262 | out << '\n'; |
263 | } |
264 | |
265 | } // end namespace benchmark |
266 | |