blob: db04183dae9a3e51e9ef520347849d8bba9f62e2 [file] [log] [blame]
Eric Fiselierb08d8b12016-07-19 23:07:03 +00001
2#undef NDEBUG
3#include "benchmark/benchmark.h"
4#include "../src/check.h" // NOTE: check.h is for internal use only!
5#include "../src/re.h" // NOTE: re.h is for internal use only
6#include <cassert>
7#include <cstring>
8#include <iostream>
9#include <sstream>
10#include <vector>
11#include <utility>
12#include <algorithm>
13#include <cmath>
14
15namespace {
16
17// ========================================================================= //
18// -------------------------- Testing Case --------------------------------- //
19// ========================================================================= //
20
21enum MatchRules {
22 MR_Default, // Skip non-matching lines until a match is found.
23 MR_Next // Match must occur on the next line.
24};
25
26struct TestCase {
27 std::string regex;
28 int match_rule;
29
30 TestCase(std::string re, int rule = MR_Default) : regex(re), match_rule(rule) {}
31
32 void Check(std::stringstream& remaining_output) const {
33 benchmark::Regex r;
34 std::string err_str;
35 r.Init(regex, &err_str);
36 CHECK(err_str.empty()) << "Could not construct regex \"" << regex << "\""
37 << " got Error: " << err_str;
38
Eric Fiselierf6e09e52016-08-09 18:56:48 +000039 std::string near = "<EOF>";
Eric Fiselierb08d8b12016-07-19 23:07:03 +000040 std::string line;
Eric Fiselierf6e09e52016-08-09 18:56:48 +000041 bool first = true;
Eric Fiselierb08d8b12016-07-19 23:07:03 +000042 while (remaining_output.eof() == false) {
43 CHECK(remaining_output.good());
44 std::getline(remaining_output, line);
Eric Fiselierf6e09e52016-08-09 18:56:48 +000045 // Keep the first line as context.
46 if (first) {
47 near = line;
48 first = false;
49 }
Eric Fiselierb08d8b12016-07-19 23:07:03 +000050 if (r.Match(line)) return;
51 CHECK(match_rule != MR_Next) << "Expected line \"" << line
Eric Fiselierf6e09e52016-08-09 18:56:48 +000052 << "\" to match regex \"" << regex << "\""
53 << "\nstarted matching at line: \"" << near << "\"";
Eric Fiselierb08d8b12016-07-19 23:07:03 +000054 }
55
56 CHECK(remaining_output.eof() == false)
57 << "End of output reached before match for regex \"" << regex
Eric Fiselierf6e09e52016-08-09 18:56:48 +000058 << "\" was found"
59 << "\nstarted matching at line: \"" << near << "\"";
Eric Fiselierb08d8b12016-07-19 23:07:03 +000060 }
61};
62
63std::vector<TestCase> ConsoleOutputTests;
64std::vector<TestCase> JSONOutputTests;
65std::vector<TestCase> CSVOutputTests;
66
67// ========================================================================= //
68// -------------------------- Test Helpers --------------------------------- //
69// ========================================================================= //
70
71class TestReporter : public benchmark::BenchmarkReporter {
72public:
73 TestReporter(std::vector<benchmark::BenchmarkReporter*> reps)
74 : reporters_(reps) {}
75
76 virtual bool ReportContext(const Context& context) {
77 bool last_ret = false;
78 bool first = true;
79 for (auto rep : reporters_) {
80 bool new_ret = rep->ReportContext(context);
81 CHECK(first || new_ret == last_ret)
82 << "Reports return different values for ReportContext";
83 first = false;
84 last_ret = new_ret;
85 }
86 return last_ret;
87 }
88
89 virtual void ReportRuns(const std::vector<Run>& report) {
90 for (auto rep : reporters_)
91 rep->ReportRuns(report);
92 }
93
94 virtual void Finalize() {
95 for (auto rep : reporters_)
96 rep->Finalize();
97 }
98
99private:
100 std::vector<benchmark::BenchmarkReporter*> reporters_;
101};
102
103
104#define CONCAT2(x, y) x##y
105#define CONCAT(x, y) CONCAT2(x, y)
106
107#define ADD_CASES(...) \
108 int CONCAT(dummy, __LINE__) = AddCases(__VA_ARGS__)
109
110int AddCases(std::vector<TestCase>* out, std::initializer_list<TestCase> const& v) {
111 for (auto const& TC : v)
112 out->push_back(TC);
113 return 0;
114}
115
116template <class First>
117std::string join(First f) { return f; }
118
119template <class First, class ...Args>
120std::string join(First f, Args&&... args) {
121 return std::string(std::move(f)) + "[ ]+" + join(std::forward<Args>(args)...);
122}
123
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000124std::string dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000125
126#define ADD_COMPLEXITY_CASES(...) \
127 int CONCAT(dummy, __LINE__) = AddComplexityTest(__VA_ARGS__)
128
129int AddComplexityTest(std::vector<TestCase>* console_out, std::vector<TestCase>* json_out,
130 std::vector<TestCase>* csv_out, std::string big_o_test_name,
131 std::string rms_test_name, std::string big_o) {
132 std::string big_o_str = dec_re + " " + big_o;
133 AddCases(console_out, {
134 {join("^" + big_o_test_name + "", big_o_str, big_o_str) + "[ ]*$"},
135 {join("^" + rms_test_name + "", "[0-9]+ %", "[0-9]+ %") + "[ ]*$"}
136 });
137 AddCases(json_out, {
138 {"\"name\": \"" + big_o_test_name + "\",$"},
139 {"\"cpu_coefficient\": [0-9]+,$", MR_Next},
140 {"\"real_coefficient\": [0-9]{1,5},$", MR_Next},
141 {"\"big_o\": \"" + big_o + "\",$", MR_Next},
142 {"\"time_unit\": \"ns\"$", MR_Next},
143 {"}", MR_Next},
144 {"\"name\": \"" + rms_test_name + "\",$"},
145 {"\"rms\": [0-9]+%$", MR_Next},
146 {"}", MR_Next}
147 });
148 AddCases(csv_out, {
149 {"^\"" + big_o_test_name + "\",," + dec_re + "," + dec_re + "," + big_o + ",,,,,$"},
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000150 {"^\"" + rms_test_name + "\",," + dec_re + "," + dec_re + ",,,,,,$", MR_Next}
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000151 });
152 return 0;
153}
154
155} // end namespace
156
157// ========================================================================= //
158// --------------------------- Testing BigO O(1) --------------------------- //
159// ========================================================================= //
160
161void BM_Complexity_O1(benchmark::State& state) {
162 while (state.KeepRunning()) {
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000163 for (int i=0; i < 1024; ++i) {
164 benchmark::DoNotOptimize(&i);
165 }
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000166 }
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000167 state.SetComplexityN(state.range(0));
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000168}
169BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity(benchmark::o1);
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000170BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity();
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000171BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity([](int){return 1.0; });
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000172
173const char* big_o_1_test_name = "BM_Complexity_O1_BigO";
174const char* rms_o_1_test_name = "BM_Complexity_O1_RMS";
175const char* enum_auto_big_o_1 = "\\([0-9]+\\)";
176const char* lambda_big_o_1 = "f\\(N\\)";
177
178// Add enum tests
179ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
180 big_o_1_test_name, rms_o_1_test_name, enum_auto_big_o_1);
181
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000182// Add auto enum tests
183ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
184 big_o_1_test_name, rms_o_1_test_name, enum_auto_big_o_1);
185
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000186// Add lambda tests
187ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
188 big_o_1_test_name, rms_o_1_test_name, lambda_big_o_1);
189
190// ========================================================================= //
191// --------------------------- Testing BigO O(N) --------------------------- //
192// ========================================================================= //
193
194std::vector<int> ConstructRandomVector(int size) {
195 std::vector<int> v;
196 v.reserve(size);
197 for (int i = 0; i < size; ++i) {
198 v.push_back(rand() % size);
199 }
200 return v;
201}
202
203void BM_Complexity_O_N(benchmark::State& state) {
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000204 auto v = ConstructRandomVector(state.range(0));
205 const int item_not_in_vector = state.range(0)*2; // Test worst case scenario (item not in vector)
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000206 while (state.KeepRunning()) {
207 benchmark::DoNotOptimize(std::find(v.begin(), v.end(), item_not_in_vector));
208 }
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000209 state.SetComplexityN(state.range(0));
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000210}
211BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oN);
212BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) -> double{return n; });
213BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity();
214
215const char* big_o_n_test_name = "BM_Complexity_O_N_BigO";
216const char* rms_o_n_test_name = "BM_Complexity_O_N_RMS";
217const char* enum_auto_big_o_n = "N";
218const char* lambda_big_o_n = "f\\(N\\)";
219
220// Add enum tests
221ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
222 big_o_n_test_name, rms_o_n_test_name, enum_auto_big_o_n);
223
224// Add lambda tests
225ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
226 big_o_n_test_name, rms_o_n_test_name, lambda_big_o_n);
227
228// ========================================================================= //
229// ------------------------- Testing BigO O(N*lgN) ------------------------- //
230// ========================================================================= //
231
232static void BM_Complexity_O_N_log_N(benchmark::State& state) {
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000233 auto v = ConstructRandomVector(state.range(0));
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000234 while (state.KeepRunning()) {
235 std::sort(v.begin(), v.end());
236 }
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000237 state.SetComplexityN(state.range(0));
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000238}
239BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oNLogN);
240BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) {return n * std::log2(n); });
241BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity();
242
243const char* big_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_BigO";
244const char* rms_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_RMS";
245const char* enum_auto_big_o_n_lg_n = "NlgN";
246const char* lambda_big_o_n_lg_n = "f\\(N\\)";
247
248// Add enum tests
249ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
250 big_o_n_lg_n_test_name, rms_o_n_lg_n_test_name, enum_auto_big_o_n_lg_n);
251
252// Add lambda tests
253ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
254 big_o_n_lg_n_test_name, rms_o_n_lg_n_test_name, lambda_big_o_n_lg_n);
255
256
257// ========================================================================= //
258// --------------------------- TEST CASES END ------------------------------ //
259// ========================================================================= //
260
261
262int main(int argc, char* argv[]) {
Eric Fiselierf6e09e52016-08-09 18:56:48 +0000263 benchmark::Initialize(&argc, argv);
264 benchmark::ConsoleReporter CR(benchmark::ConsoleReporter::OO_None);
Eric Fiselierb08d8b12016-07-19 23:07:03 +0000265 benchmark::JSONReporter JR;
266 benchmark::CSVReporter CSVR;
267 struct ReporterTest {
268 const char* name;
269 std::vector<TestCase>& output_cases;
270 benchmark::BenchmarkReporter& reporter;
271 std::stringstream out_stream;
272 std::stringstream err_stream;
273
274 ReporterTest(const char* n,
275 std::vector<TestCase>& out_tc,
276 benchmark::BenchmarkReporter& br)
277 : name(n), output_cases(out_tc), reporter(br) {
278 reporter.SetOutputStream(&out_stream);
279 reporter.SetErrorStream(&err_stream);
280 }
281 } TestCases[] = {
282 {"ConsoleReporter", ConsoleOutputTests, CR},
283 {"JSONReporter", JSONOutputTests, JR},
284 {"CSVReporter", CSVOutputTests, CSVR}
285 };
286
287 // Create the test reporter and run the benchmarks.
288 std::cout << "Running benchmarks...\n";
289 TestReporter test_rep({&CR, &JR, &CSVR});
290 benchmark::RunSpecifiedBenchmarks(&test_rep);
291
292 for (auto& rep_test : TestCases) {
293 std::string msg = std::string("\nTesting ") + rep_test.name + " Output\n";
294 std::string banner(msg.size() - 1, '-');
295 std::cout << banner << msg << banner << "\n";
296
297 std::cerr << rep_test.err_stream.str();
298 std::cout << rep_test.out_stream.str();
299
300 for (const auto& TC : rep_test.output_cases)
301 TC.Check(rep_test.out_stream);
302
303 std::cout << "\n";
304 }
305 return 0;
306}
307