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