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 <algorithm> |
16 | #include <cstdint> |
17 | #include <iostream> |
18 | #include <string> |
19 | #include <tuple> |
20 | #include <vector> |
21 | |
22 | #include "benchmark/benchmark.h" |
23 | #include "check.h" |
24 | #include "complexity.h" |
25 | #include "string_util.h" |
26 | #include "timers.h" |
27 | |
28 | // File format reference: http://edoceo.com/utilitas/csv-file-format. |
29 | |
30 | namespace benchmark { |
31 | |
32 | namespace { |
33 | std::vector<std::string> elements = { |
34 | "name" , "iterations" , "real_time" , "cpu_time" , |
35 | "time_unit" , "bytes_per_second" , "items_per_second" , "label" , |
36 | "error_occurred" , "error_message" }; |
37 | } // namespace |
38 | |
39 | std::string CsvEscape(const std::string& s) { |
40 | std::string tmp; |
41 | tmp.reserve(res: s.size() + 2); |
42 | for (char c : s) { |
43 | switch (c) { |
44 | case '"': |
45 | tmp += "\"\"" ; |
46 | break; |
47 | default: |
48 | tmp += c; |
49 | break; |
50 | } |
51 | } |
52 | return '"' + tmp + '"'; |
53 | } |
54 | |
55 | BENCHMARK_EXPORT |
56 | bool CSVReporter::ReportContext(const Context& context) { |
57 | PrintBasicContext(out: &GetErrorStream(), context); |
58 | return true; |
59 | } |
60 | |
61 | BENCHMARK_EXPORT |
62 | void CSVReporter::ReportRuns(const std::vector<Run>& reports) { |
63 | std::ostream& Out = GetOutputStream(); |
64 | |
65 | if (!printed_header_) { |
66 | // save the names of all the user counters |
67 | for (const auto& run : reports) { |
68 | for (const auto& cnt : run.counters) { |
69 | if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second" ) |
70 | continue; |
71 | user_counter_names_.insert(x: cnt.first); |
72 | } |
73 | } |
74 | |
75 | // print the header |
76 | for (auto B = elements.begin(); B != elements.end();) { |
77 | Out << *B++; |
78 | if (B != elements.end()) Out << "," ; |
79 | } |
80 | for (auto B = user_counter_names_.begin(); |
81 | B != user_counter_names_.end();) { |
82 | Out << ",\"" << *B++ << "\"" ; |
83 | } |
84 | Out << "\n" ; |
85 | |
86 | printed_header_ = true; |
87 | } else { |
88 | // check that all the current counters are saved in the name set |
89 | for (const auto& run : reports) { |
90 | for (const auto& cnt : run.counters) { |
91 | if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second" ) |
92 | continue; |
93 | BM_CHECK(user_counter_names_.find(cnt.first) != |
94 | user_counter_names_.end()) |
95 | << "All counters must be present in each run. " |
96 | << "Counter named \"" << cnt.first |
97 | << "\" was not in a run after being added to the header" ; |
98 | } |
99 | } |
100 | } |
101 | |
102 | // print results for each run |
103 | for (const auto& run : reports) { |
104 | PrintRunData(report: run); |
105 | } |
106 | } |
107 | |
108 | BENCHMARK_EXPORT |
109 | void CSVReporter::PrintRunData(const Run& run) { |
110 | std::ostream& Out = GetOutputStream(); |
111 | Out << CsvEscape(s: run.benchmark_name()) << "," ; |
112 | if (run.skipped) { |
113 | Out << std::string(elements.size() - 3, ','); |
114 | Out << std::boolalpha << (internal::SkippedWithError == run.skipped) << "," ; |
115 | Out << CsvEscape(s: run.skip_message) << "\n" ; |
116 | return; |
117 | } |
118 | |
119 | // Do not print iteration on bigO and RMS report |
120 | if (!run.report_big_o && !run.report_rms) { |
121 | Out << run.iterations; |
122 | } |
123 | Out << "," ; |
124 | |
125 | if (run.run_type != Run::RT_Aggregate || |
126 | run.aggregate_unit == StatisticUnit::kTime) { |
127 | Out << run.GetAdjustedRealTime() << "," ; |
128 | Out << run.GetAdjustedCPUTime() << "," ; |
129 | } else { |
130 | assert(run.aggregate_unit == StatisticUnit::kPercentage); |
131 | Out << run.real_accumulated_time << "," ; |
132 | Out << run.cpu_accumulated_time << "," ; |
133 | } |
134 | |
135 | // Do not print timeLabel on bigO and RMS report |
136 | if (run.report_big_o) { |
137 | Out << GetBigOString(complexity: run.complexity); |
138 | } else if (!run.report_rms && |
139 | run.aggregate_unit != StatisticUnit::kPercentage) { |
140 | Out << GetTimeUnitString(unit: run.time_unit); |
141 | } |
142 | Out << "," ; |
143 | |
144 | if (run.counters.find(x: "bytes_per_second" ) != run.counters.end()) { |
145 | Out << run.counters.at(k: "bytes_per_second" ); |
146 | } |
147 | Out << "," ; |
148 | if (run.counters.find(x: "items_per_second" ) != run.counters.end()) { |
149 | Out << run.counters.at(k: "items_per_second" ); |
150 | } |
151 | Out << "," ; |
152 | if (!run.report_label.empty()) { |
153 | Out << CsvEscape(s: run.report_label); |
154 | } |
155 | Out << ",," ; // for error_occurred and error_message |
156 | |
157 | // Print user counters |
158 | for (const auto& ucn : user_counter_names_) { |
159 | auto it = run.counters.find(x: ucn); |
160 | if (it == run.counters.end()) { |
161 | Out << "," ; |
162 | } else { |
163 | Out << "," << it->second; |
164 | } |
165 | } |
166 | Out << '\n'; |
167 | } |
168 | |
169 | } // end namespace benchmark |
170 | |