CPPX 2.1.0
A Modern C++ Utility Library
Loading...
Searching...
No Matches
benchmark_main.cpp
Go to the documentation of this file.
1
2
3#include "../include/cppx.h"
4
5#include <algorithm>
6#include <chrono>
7#include <cmath>
8#include <cstddef>
9#include <fstream>
10#include <iomanip>
11#include <iostream>
12#include <map>
13#include <random>
14#include <set>
15#include <sstream>
16#include <string>
17#include <unordered_set>
18#include <vector>
19
21{
22 public:
23 using Clock = std::chrono::steady_clock;
24 using TimePoint = Clock::time_point;
25 using Duration = std::chrono::duration<double, std::milli>;
26
27 void start()
28 {
29 m_start = Clock::now();
30 }
31
32 void stop()
33 {
34 m_end = Clock::now();
35 m_elapsed = std::chrono::duration_cast<Duration>(m_end - m_start);
36 }
37
38 double elapsed_ms() const
39 {
40 return m_elapsed.count();
41 }
42
43 private:
44 TimePoint m_start{};
45 TimePoint m_end{};
46 Duration m_elapsed{};
47};
48
49static std::vector<int> generate_unique_random(std::size_t count, unsigned seed = 42)
50{
51 std::mt19937 rng(seed);
52 std::unordered_set<int> unique_set;
53 unique_set.reserve(static_cast<std::size_t>(count * 1.3));
54 while (unique_set.size() < count)
55 unique_set.insert(static_cast<int>(rng()));
56 return {unique_set.begin(), unique_set.end()};
57}
58
59static std::vector<int> pick_random(const std::vector<int> &source, std::size_t count, unsigned seed = 123)
60{
61 std::mt19937 rng(seed);
62 std::vector<int> copy = source;
63 std::shuffle(copy.begin(), copy.end(), rng);
64 copy.resize(std::min(count, copy.size()));
65 return copy;
66}
67
69{
70 std::string structure;
71 std::size_t n;
72 double insert_ms;
73 double lookup_ms;
74 double delete_ms;
75};
76
77static const int WARMUP_RUNS = 1;
78static const int BENCH_RUNS = 3;
79
80template <typename SetupFn, typename BenchFn> static double run_timed(SetupFn setup, BenchFn bench, int runs)
81{
82 setup();
83 Benchmark bm;
84
85 std::vector<double> times;
86 times.reserve(static_cast<std::size_t>(runs));
87 for (int i = 0; i < runs; ++i)
88 {
89 setup();
90 bm.start();
91 bench();
92 bm.stop();
93 times.push_back(bm.elapsed_ms());
94 }
95 std::sort(times.begin(), times.end());
96 return times[static_cast<std::size_t>(runs / 2)];
97}
98
99static BenchmarkResult bench_std_set(std::size_t n, const std::vector<int> &data, const std::vector<int> &lookup_data,
100 const std::vector<int> &delete_data)
101{
102 Benchmark bm;
103 std::set<int> s;
104
105 for (int i = 0; i < WARMUP_RUNS; ++i)
106 {
107 s.clear();
108 for (int v : data)
109 s.insert(v);
110 }
111
112 std::vector<double> ins_times, lkp_times, del_times;
113
114 for (int i = 0; i < BENCH_RUNS; ++i)
115 {
116 s.clear();
117 bm.start();
118 for (int v : data)
119 s.insert(v);
120 bm.stop();
121 ins_times.push_back(bm.elapsed_ms());
122
123 bm.start();
124 volatile bool sink = false;
125 for (int v : lookup_data)
126 sink = (s.find(v) != s.end());
127 bm.stop();
128 lkp_times.push_back(bm.elapsed_ms());
129 (void)sink;
130
131 bm.start();
132 for (int v : delete_data)
133 s.erase(v);
134 bm.stop();
135 del_times.push_back(bm.elapsed_ms());
136 }
137
138 std::sort(ins_times.begin(), ins_times.end());
139 std::sort(lkp_times.begin(), lkp_times.end());
140 std::sort(del_times.begin(), del_times.end());
141
142 return {"std::set", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
143}
144
145static BenchmarkResult bench_std_map(std::size_t n, const std::vector<int> &data, const std::vector<int> &lookup_data,
146 const std::vector<int> &delete_data)
147{
148 Benchmark bm;
149 std::map<int, int> m;
150
151 for (int i = 0; i < WARMUP_RUNS; ++i)
152 {
153 m.clear();
154 for (int v : data)
155 m[v] = v;
156 }
157
158 std::vector<double> ins_times, lkp_times, del_times;
159
160 for (int i = 0; i < BENCH_RUNS; ++i)
161 {
162 m.clear();
163 bm.start();
164 for (int v : data)
165 m[v] = v;
166 bm.stop();
167 ins_times.push_back(bm.elapsed_ms());
168
169 bm.start();
170 volatile bool sink = false;
171 for (int v : lookup_data)
172 sink = (m.find(v) != m.end());
173 bm.stop();
174 lkp_times.push_back(bm.elapsed_ms());
175 (void)sink;
176
177 bm.start();
178 for (int v : delete_data)
179 m.erase(v);
180 bm.stop();
181 del_times.push_back(bm.elapsed_ms());
182 }
183
184 std::sort(ins_times.begin(), ins_times.end());
185 std::sort(lkp_times.begin(), lkp_times.end());
186 std::sort(del_times.begin(), del_times.end());
187
188 return {"std::map", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
189}
190
191static BenchmarkResult bench_std_unordered_set(std::size_t n, const std::vector<int> &data,
192 const std::vector<int> &lookup_data, const std::vector<int> &delete_data)
193{
194 Benchmark bm;
195 std::unordered_set<int> s;
196 s.reserve(n);
197
198 for (int i = 0; i < WARMUP_RUNS; ++i)
199 {
200 s.clear();
201 s.reserve(n);
202 for (int v : data)
203 s.insert(v);
204 }
205
206 std::vector<double> ins_times, lkp_times, del_times;
207
208 for (int i = 0; i < BENCH_RUNS; ++i)
209 {
210 s.clear();
211 s.reserve(n);
212 bm.start();
213 for (int v : data)
214 s.insert(v);
215 bm.stop();
216 ins_times.push_back(bm.elapsed_ms());
217
218 bm.start();
219 volatile bool sink = false;
220 for (int v : lookup_data)
221 sink = (s.find(v) != s.end());
222 bm.stop();
223 lkp_times.push_back(bm.elapsed_ms());
224 (void)sink;
225
226 bm.start();
227 for (int v : delete_data)
228 s.erase(v);
229 bm.stop();
230 del_times.push_back(bm.elapsed_ms());
231 }
232
233 std::sort(ins_times.begin(), ins_times.end());
234 std::sort(lkp_times.begin(), lkp_times.end());
235 std::sort(del_times.begin(), del_times.end());
236
237 return {"std::unordered_set", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
238}
239
240static BenchmarkResult bench_avl(std::size_t n, const std::vector<int> &data, const std::vector<int> &lookup_data,
241 const std::vector<int> &delete_data)
242{
243 Benchmark bm;
244 std::vector<double> ins_times, lkp_times, del_times;
245
246 for (int i = 0; i < WARMUP_RUNS; ++i)
247 {
249 for (int v : data)
250 warmup.insert(v);
251 }
252
253 for (int i = 0; i < BENCH_RUNS; ++i)
254 {
256 bm.start();
257 for (int v : data)
258 tree.insert(v);
259 bm.stop();
260 ins_times.push_back(bm.elapsed_ms());
261
262 bm.start();
263 volatile bool sink = false;
264 for (int v : lookup_data)
265 sink = tree.contains(v);
266 bm.stop();
267 lkp_times.push_back(bm.elapsed_ms());
268 (void)sink;
269
270 bm.start();
271 for (int v : delete_data)
272 tree.remove(v);
273 bm.stop();
274 del_times.push_back(bm.elapsed_ms());
275 }
276
277 std::sort(ins_times.begin(), ins_times.end());
278 std::sort(lkp_times.begin(), lkp_times.end());
279 std::sort(del_times.begin(), del_times.end());
280
281 return {"stl_ext::AVLTree", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
282}
283
284static BenchmarkResult bench_rbt(std::size_t n, const std::vector<int> &data, const std::vector<int> &lookup_data,
285 const std::vector<int> &delete_data)
286{
287 Benchmark bm;
288 std::vector<double> ins_times, lkp_times, del_times;
289
290 for (int i = 0; i < WARMUP_RUNS; ++i)
291 {
293 for (int v : data)
294 warmup.insert(v);
295 }
296
297 for (int i = 0; i < BENCH_RUNS; ++i)
298 {
300 bm.start();
301 for (int v : data)
302 tree.insert(v);
303 bm.stop();
304 ins_times.push_back(bm.elapsed_ms());
305
306 bm.start();
307 volatile bool sink = false;
308 for (int v : lookup_data)
309 sink = tree.contains(v);
310 bm.stop();
311 lkp_times.push_back(bm.elapsed_ms());
312 (void)sink;
313
314 bm.start();
315 for (int v : delete_data)
316 tree.remove(v);
317 bm.stop();
318 del_times.push_back(bm.elapsed_ms());
319 }
320
321 std::sort(ins_times.begin(), ins_times.end());
322 std::sort(lkp_times.begin(), lkp_times.end());
323 std::sort(del_times.begin(), del_times.end());
324
325 return {"stl_ext::RBTree", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
326}
327
328static BenchmarkResult bench_bst(std::size_t n, const std::vector<int> &data, const std::vector<int> &lookup_data,
329 const std::vector<int> &delete_data)
330{
331 Benchmark bm;
332 std::vector<double> ins_times, lkp_times, del_times;
333
334 for (int i = 0; i < WARMUP_RUNS; ++i)
335 {
336 stl_ext::BST<int> warmup;
337 for (int v : data)
338 warmup.insert(v);
339 }
340
341 for (int i = 0; i < BENCH_RUNS; ++i)
342 {
344 bm.start();
345 for (int v : data)
346 tree.insert(v);
347 bm.stop();
348 ins_times.push_back(bm.elapsed_ms());
349
350 bm.start();
351 volatile bool sink = false;
352 for (int v : lookup_data)
353 sink = tree.contains(v);
354 bm.stop();
355 lkp_times.push_back(bm.elapsed_ms());
356 (void)sink;
357
358 bm.start();
359 for (int v : delete_data)
360 tree.remove(v);
361 bm.stop();
362 del_times.push_back(bm.elapsed_ms());
363 }
364
365 std::sort(ins_times.begin(), ins_times.end());
366 std::sort(lkp_times.begin(), lkp_times.end());
367 std::sort(del_times.begin(), del_times.end());
368
369 return {"stl_ext::BST", n, ins_times[BENCH_RUNS / 2], lkp_times[BENCH_RUNS / 2], del_times[BENCH_RUNS / 2]};
370}
371
372static std::string format_n(std::size_t n)
373{
374 if (n >= 1'000'000)
375 return std::to_string(n / 1'000'000) + "M";
376 if (n >= 1'000)
377 return std::to_string(n / 1'000) + "K";
378 return std::to_string(n);
379}
380
381static void print_separator(int w)
382{
383 std::cout << std::string(static_cast<std::size_t>(w), '-') << "\n";
384}
385
386static void print_table(const std::vector<BenchmarkResult> &results)
387{
388 const int w_s = 22, w_n = 8, w_t = 16;
389 const int w_total = w_s + w_n + w_t * 3 + 6;
390
391 std::cout << "\n";
392 print_separator(w_total);
393 std::cout << std::left << std::setw(w_s) << "Structure" << std::right << std::setw(w_n) << "N" << std::setw(w_t)
394 << "Insert (ms)" << std::setw(w_t) << "Lookup (ms)" << std::setw(w_t) << "Delete (ms)" << "\n";
395 print_separator(w_total);
396
397 for (const auto &r : results)
398 {
399 std::cout << std::left << std::setw(w_s) << r.structure << std::right << std::setw(w_n) << format_n(r.n)
400 << std::setw(w_t) << std::fixed << std::setprecision(2) << r.insert_ms << std::setw(w_t)
401 << r.lookup_ms << std::setw(w_t) << r.delete_ms << "\n";
402 }
403 print_separator(w_total);
404 std::cout << "\n";
405}
406
407static void export_csv(const std::vector<BenchmarkResult> &results, const std::string &filename)
408{
409 std::ofstream out(filename);
410 if (!out.is_open())
411 {
412 std::cerr << "[error] Cannot open " << filename << " for writing.\n";
413 return;
414 }
415 out << "structure,n,insert_ms,lookup_ms,delete_ms\n";
416 for (const auto &r : results)
417 {
418 out << r.structure << "," << r.n << "," << std::fixed << std::setprecision(4) << r.insert_ms << ","
419 << r.lookup_ms << "," << r.delete_ms << "\n";
420 }
421 std::cout << "[info] CSV exported to " << filename << "\n";
422}
423
424static std::string svg_color(const std::string &structure)
425{
426 if (structure == "std::set")
427 return "#6366f1";
428 if (structure == "std::map")
429 return "#8b5cf6";
430 if (structure == "std::unordered_set")
431 return "#a78bfa";
432 if (structure == "stl_ext::AVLTree")
433 return "#06b6d4";
434 if (structure == "stl_ext::RBTree")
435 return "#10b981";
436 return "#f59e0b";
437}
438
439static std::string svg_escape(const std::string &s)
440{
441 std::string out;
442 for (char c : s)
443 {
444 switch (c)
445 {
446 case '&':
447 out += "&amp;";
448 break;
449 case '<':
450 out += "&lt;";
451 break;
452 case '>':
453 out += "&gt;";
454 break;
455 case '"':
456 out += "&quot;";
457 break;
458 default:
459 out += c;
460 }
461 }
462 return out;
463}
464
465static std::string to_str(double v, int prec = 1)
466{
467 std::ostringstream ss;
468 ss << std::fixed << std::setprecision(prec) << v;
469 return ss.str();
470}
471
472static void emit_subchart(std::ostream &svg, const std::string &title, const std::vector<BenchmarkResult> &results,
473 const std::vector<std::string> &structures, const std::vector<std::size_t> &sizes,
474 double (*value_fn)(const BenchmarkResult &), double ox, double oy, double chart_w,
475 double chart_h)
476{
477
478 double max_val = 0;
479 for (auto &r : results)
480 max_val = std::max(max_val, value_fn(r));
481 if (max_val < 1e-9)
482 max_val = 1;
483 max_val *= 1.15;
484
485 const double margin_left = 60;
486 const double margin_bottom = 40;
487 const double margin_top = 35;
488 const double plot_w = chart_w - margin_left - 10;
489 const double plot_h = chart_h - margin_bottom - margin_top;
490
491 double px = ox + margin_left;
492 double py = oy + margin_top;
493
494 svg << "<rect x=\"" << ox << "\" y=\"" << oy << "\" width=\"" << chart_w << "\" height=\"" << chart_h
495 << "\" rx=\"8\" fill=\"#1e293b\"/>\n";
496
497 svg << "<text x=\"" << ox + chart_w / 2 << "\" y=\"" << oy + 22
498 << "\" text-anchor=\"middle\" font-size=\"14\" font-weight=\"bold\" fill=\"white\">" << title << "</text>\n";
499
500 for (int i = 0; i <= 4; ++i)
501 {
502 double frac = i / 4.0;
503 double yy = py + plot_h - frac * plot_h;
504 double label_val = frac * max_val;
505 svg << "<line x1=\"" << px << "\" y1=\"" << yy << "\" x2=\"" << px + plot_w << "\" y2=\"" << yy
506 << "\" stroke=\"#334155\" stroke-width=\"0.5\"/>\n";
507 svg << "<text x=\"" << px - 6 << "\" y=\"" << yy + 4
508 << "\" text-anchor=\"end\" font-size=\"9\" fill=\"#94a3b8\">" << to_str(label_val, (max_val > 100 ? 0 : 1))
509 << "</text>\n";
510 }
511
512 svg << "<text x=\"" << ox + 14 << "\" y=\"" << py + plot_h / 2
513 << "\" text-anchor=\"middle\" font-size=\"10\" fill=\"#94a3b8\" " << "transform=\"rotate(-90," << ox + 14 << ","
514 << py + plot_h / 2 << ")\">Time (ms)</text>\n";
515
516 std::size_t n_groups = sizes.size();
517 std::size_t n_bars = structures.size();
518 double group_w = plot_w / static_cast<double>(n_groups);
519 double bar_w = group_w / (static_cast<double>(n_bars) + 1.5);
520 double gap = bar_w * 0.25;
521
522 for (std::size_t gi = 0; gi < n_groups; ++gi)
523 {
524 double gx = px + gi * group_w;
525
526 svg << "<text x=\"" << gx + group_w / 2 << "\" y=\"" << py + plot_h + 18
527 << "\" text-anchor=\"middle\" font-size=\"11\" fill=\"#94a3b8\">" << format_n(sizes[gi]) << "</text>\n";
528
529 std::size_t bar_idx = 0;
530 for (std::size_t si = 0; si < n_bars; ++si)
531 {
532
533 const BenchmarkResult *match = nullptr;
534 for (auto &r : results)
535 {
536 if (r.structure == structures[si] && r.n == sizes[gi])
537 {
538 match = &r;
539 break;
540 }
541 }
542 if (!match)
543 continue;
544
545 double val = value_fn(*match);
546 double bh = (val / max_val) * plot_h;
547 double bx = gx + gap + bar_idx * (bar_w + gap);
548 double by = py + plot_h - bh;
549
550 svg << "<rect x=\"" << bx << "\" y=\"" << by << "\" width=\"" << bar_w << "\" height=\"" << bh
551 << "\" rx=\"2\" fill=\"" << svg_color(structures[si]) << "\" opacity=\"0.9\"/>\n";
552
553 svg << "<text x=\"" << bx + bar_w / 2 << "\" y=\"" << by - 3
554 << "\" text-anchor=\"middle\" font-size=\"8\" font-weight=\"bold\" fill=\"white\">" << to_str(val)
555 << "</text>\n";
556
557 ++bar_idx;
558 }
559 }
560}
561
562static void generate_svg(const std::vector<BenchmarkResult> &results, const std::string &filename)
563{
564
565 std::vector<std::string> structures;
566 std::vector<std::size_t> sizes;
567 for (auto &r : results)
568 {
569 if (std::find(structures.begin(), structures.end(), r.structure) == structures.end())
570 structures.push_back(r.structure);
571 if (std::find(sizes.begin(), sizes.end(), r.n) == sizes.end())
572 sizes.push_back(r.n);
573 }
574 std::sort(sizes.begin(), sizes.end());
575
576 const double chart_w = 380;
577 const double chart_h = 300;
578 const double pad = 20;
579 const double legend_h = 60;
580 const double title_h = 40;
581 const double total_w = 3 * chart_w + 4 * pad;
582 const double total_h = title_h + chart_h + legend_h + 2 * pad;
583
584 std::ofstream f(filename);
585 if (!f.is_open())
586 {
587 std::cerr << "[error] Cannot write " << filename << "\n";
588 return;
589 }
590
591 f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
592 f << "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 " << total_w << " " << total_h << "\">\n";
593 f << "<style>text { font-family: 'Segoe UI', Roboto, Arial, sans-serif; }</style>\n";
594
595 f << "<rect width=\"100%\" height=\"100%\" rx=\"12\" fill=\"#0f172a\"/>\n";
596
597 f << "<text x=\"" << total_w / 2 << "\" y=\"28\" text-anchor=\"middle\" font-size=\"20\" "
598 << "font-weight=\"bold\" fill=\"white\">CPPX Benchmark Results</text>\n";
599
600 auto insert_fn = [](const BenchmarkResult &r) -> double { return r.insert_ms; };
601 auto lookup_fn = [](const BenchmarkResult &r) -> double { return r.lookup_ms; };
602 auto delete_fn = [](const BenchmarkResult &r) -> double { return r.delete_ms; };
603
604 double y_charts = title_h;
605 emit_subchart(f, "Insert Time", results, structures, sizes, insert_fn, pad, y_charts, chart_w, chart_h);
606 emit_subchart(f, "Lookup Time", results, structures, sizes, lookup_fn, pad + chart_w + pad, y_charts, chart_w,
607 chart_h);
608 emit_subchart(f, "Delete Time", results, structures, sizes, delete_fn, pad + 2 * (chart_w + pad), y_charts, chart_w,
609 chart_h);
610
611 double ly = y_charts + chart_h + 18;
612 double item_w = 170;
613 double lx_start = total_w / 2 - (structures.size() * item_w) / 2.0;
614 for (std::size_t i = 0; i < structures.size(); ++i)
615 {
616 double lx = lx_start + i * item_w;
617 f << "<rect x=\"" << lx << "\" y=\"" << ly << "\" width=\"14\" height=\"14\" rx=\"3\" fill=\""
618 << svg_color(structures[i]) << "\"/>\n";
619 f << "<text x=\"" << lx + 20 << "\" y=\"" << ly + 12 << "\" font-size=\"12\" fill=\"white\">"
620 << svg_escape(structures[i]) << "</text>\n";
621 }
622
623 f << "</svg>\n";
624 std::cout << "[info] SVG chart saved to " << filename << "\n";
625}
626
627int main(int argc, char *argv[])
628{
629 std::string out_dir = ".";
630 if (argc > 1)
631 out_dir = argv[1];
632
633 const std::vector<std::size_t> sizes = {10'000, 100'000, 1'000'000};
634
635 std::vector<BenchmarkResult> all_results;
636 all_results.reserve(sizes.size() * 6);
637
638 std::cout << "╔══════════════════════════════════════════════════════════════╗\n";
639 std::cout << "║ CPPX Benchmark Suite v3.0 ║\n";
640 std::cout << "║ Arena allocator · Iterative AVL · Raw pointers ║\n";
641 std::cout << "╚══════════════════════════════════════════════════════════════╝\n";
642
643 for (std::size_t n : sizes)
644 {
645 std::cout << "\n▶ Benchmarking N = " << format_n(n) << " (" << n << " elements) ...\n";
646
647 auto data = generate_unique_random(n);
648 auto lookup_data = pick_random(data, n / 2);
649 auto delete_data = pick_random(data, n / 10);
650
651 std::mt19937 rng(99);
652 std::shuffle(data.begin(), data.end(), rng);
653
654 std::cout << " ├─ std::set ... " << std::flush;
655 auto r1 = bench_std_set(n, data, lookup_data, delete_data);
656 std::cout << "done\n";
657
658 std::cout << " ├─ std::map ... " << std::flush;
659 auto r_map = bench_std_map(n, data, lookup_data, delete_data);
660 std::cout << "done\n";
661
662 std::cout << " ├─ std::unordered_set ... " << std::flush;
663 auto r_uset = bench_std_unordered_set(n, data, lookup_data, delete_data);
664 std::cout << "done\n";
665
666 std::cout << " ├─ stl_ext::AVLTree ... " << std::flush;
667 auto r2 = bench_avl(n, data, lookup_data, delete_data);
668 std::cout << "done\n";
669
670 std::cout << " ├─ stl_ext::RBTree ... " << std::flush;
671 auto r_rbt = bench_rbt(n, data, lookup_data, delete_data);
672 std::cout << "done\n";
673
674 if (n <= 100'000)
675 {
676 std::cout << " └─ stl_ext::BST ... " << std::flush;
677 auto r3 = bench_bst(n, data, lookup_data, delete_data);
678 std::cout << "done\n";
679 all_results.push_back(r3);
680 }
681 else
682 {
683 std::cout << " └─ stl_ext::BST ... skipped (N too large for unbalanced tree)\n";
684 }
685
686 all_results.push_back(r1);
687 all_results.push_back(r_map);
688 all_results.push_back(r_uset);
689 all_results.push_back(r2);
690 all_results.push_back(r_rbt);
691 }
692
693 std::sort(all_results.begin(), all_results.end(), [](const BenchmarkResult &a, const BenchmarkResult &b) {
694 if (a.n != b.n)
695 return a.n < b.n;
696 return a.structure < b.structure;
697 });
698
699 print_table(all_results);
700 export_csv(all_results, out_dir + "/docs/benchmark_results.csv");
701 generate_svg(all_results, out_dir + "/docs/benchmark_chart.svg");
702
703 return 0;
704}
int main(int argc, char *argv[])
std::chrono::duration< double, std::milli > Duration
double elapsed_ms() const
Clock::time_point TimePoint
std::chrono::steady_clock Clock
std::string structure