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