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(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 | bool CSVReporter::ReportContext(const Context& context) { |
56 | PrintBasicContext(&GetErrorStream(), context); |
57 | return true; |
58 | } |
59 | |
60 | void CSVReporter::ReportRuns(const std::vector<Run>& reports) { |
61 | std::ostream& Out = GetOutputStream(); |
62 | |
63 | if (!printed_header_) { |
64 | // save the names of all the user counters |
65 | for (const auto& run : reports) { |
66 | for (const auto& cnt : run.counters) { |
67 | if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second" ) |
68 | continue; |
69 | user_counter_names_.insert(cnt.first); |
70 | } |
71 | } |
72 | |
73 | // print the header |
74 | for (auto B = elements.begin(); B != elements.end();) { |
75 | Out << *B++; |
76 | if (B != elements.end()) Out << "," ; |
77 | } |
78 | for (auto B = user_counter_names_.begin(); |
79 | B != user_counter_names_.end();) { |
80 | Out << ",\"" << *B++ << "\"" ; |
81 | } |
82 | Out << "\n" ; |
83 | |
84 | printed_header_ = true; |
85 | } else { |
86 | // check that all the current counters are saved in the name set |
87 | for (const auto& run : reports) { |
88 | for (const auto& cnt : run.counters) { |
89 | if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second" ) |
90 | continue; |
91 | BM_CHECK(user_counter_names_.find(cnt.first) != |
92 | user_counter_names_.end()) |
93 | << "All counters must be present in each run. " |
94 | << "Counter named \"" << cnt.first |
95 | << "\" was not in a run after being added to the header" ; |
96 | } |
97 | } |
98 | } |
99 | |
100 | // print results for each run |
101 | for (const auto& run : reports) { |
102 | PrintRunData(run); |
103 | } |
104 | } |
105 | |
106 | void CSVReporter::PrintRunData(const Run& run) { |
107 | std::ostream& Out = GetOutputStream(); |
108 | Out << CsvEscape(run.benchmark_name()) << "," ; |
109 | if (run.error_occurred) { |
110 | Out << std::string(elements.size() - 3, ','); |
111 | Out << "true," ; |
112 | Out << CsvEscape(run.error_message) << "\n" ; |
113 | return; |
114 | } |
115 | |
116 | // Do not print iteration on bigO and RMS report |
117 | if (!run.report_big_o && !run.report_rms) { |
118 | Out << run.iterations; |
119 | } |
120 | Out << "," ; |
121 | |
122 | Out << run.GetAdjustedRealTime() << "," ; |
123 | Out << run.GetAdjustedCPUTime() << "," ; |
124 | |
125 | // Do not print timeLabel on bigO and RMS report |
126 | if (run.report_big_o) { |
127 | Out << GetBigOString(run.complexity); |
128 | } else if (!run.report_rms) { |
129 | Out << GetTimeUnitString(run.time_unit); |
130 | } |
131 | Out << "," ; |
132 | |
133 | if (run.counters.find("bytes_per_second" ) != run.counters.end()) { |
134 | Out << run.counters.at("bytes_per_second" ); |
135 | } |
136 | Out << "," ; |
137 | if (run.counters.find("items_per_second" ) != run.counters.end()) { |
138 | Out << run.counters.at("items_per_second" ); |
139 | } |
140 | Out << "," ; |
141 | if (!run.report_label.empty()) { |
142 | Out << CsvEscape(run.report_label); |
143 | } |
144 | Out << ",," ; // for error_occurred and error_message |
145 | |
146 | // Print user counters |
147 | for (const auto& ucn : user_counter_names_) { |
148 | auto it = run.counters.find(ucn); |
149 | if (it == run.counters.end()) { |
150 | Out << "," ; |
151 | } else { |
152 | Out << "," << it->second; |
153 | } |
154 | } |
155 | Out << '\n'; |
156 | } |
157 | |
158 | } // end namespace benchmark |
159 | |