-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathparser_bench.cpp
More file actions
152 lines (132 loc) · 5.12 KB
/
Copy pathparser_bench.cpp
File metadata and controls
152 lines (132 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/// @file parser_bench.cpp
/// @brief Benchmarking suite for the ITCH message parser using Google Benchmark.
///
/// This file defines a set of benchmarks to evaluate the performance of the
/// ITCH message parser implemented in the Parser class. It uses the Google
/// Benchmark library to measure execution time and throughput for different
/// parsing strategies, including callback-based parsing, collecting all parsed
/// messages, and filtering specific message types.
///
/// The benchmark fixture `ParserBenchmark` is responsible for loading the ITCH
/// data file into memory once per benchmark run, ensuring that file I/O does
/// not skew the parsing performance measurements.
///
/// Usage:
/// ./parser_bench.exe <path_to_itch_data_file> [google benchmark options]
///
/// Example:
/// ./parser_bench.exe data/itch_data.bin --benchmark_filter=BM_ParseWithCallback
///
/// Note:
/// Ensure that the Google Benchmark library is properly linked during
/// compilation.
#include <benchmark/benchmark.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "itch/parser.hpp"
namespace data {
// NOLINTNEXTLINE
std::string g_data_filename {};
} // namespace data
constexpr double KILOBYTE = 1024.0;
constexpr double MEGABYTE = KILOBYTE * KILOBYTE;
// A benchmark fixture to load the specified data file once.
class ParserBenchmark : public benchmark::Fixture {
public:
std::vector<char> itch_data;
itch::Parser parser;
void SetUp(::benchmark::State& state) override {
if (data::g_data_filename.empty()) {
state.SkipWithError(
"ITCH data file not provided. Pass the file path as a "
"command-line argument."
);
return;
}
std::ifstream file(data::g_data_filename, std::ios::binary);
if (!file) {
state.SkipWithError(("Failed to open ITCH data file: " + data::g_data_filename));
return;
}
file.seekg(0, std::ios::end);
auto size = file.tellg();
file.seekg(0, std::ios::beg);
itch_data.resize(size);
file.read(itch_data.data(), size);
// Report the size of the data being processed.
state.SetBytesProcessed(0); // Clear any previous settings
double size_mb = static_cast<double>(size) / MEGABYTE;
state.counters["FileSizeMB"] =
benchmark::Counter(size_mb, benchmark::Counter::kIsIterationInvariant);
}
void TearDown([[maybe_unused]] const ::benchmark::State& state) override {
// Clear the memory.
itch_data.clear();
itch_data.shrink_to_fit();
}
};
BENCHMARK_F(ParserBenchmark, BM_ParseWithCallback)(benchmark::State& state) {
size_t total_bytes = 0;
for ([[maybe_unused]] auto iter : state) {
size_t message_count = 0;
auto callback = [&](const itch::Message& msg) {
// NOLINTNEXTLINE
benchmark::DoNotOptimize(std::move(const_cast<itch::Message&>(msg)));
++message_count;
};
parser.parse(itch_data.data(), itch_data.size(), callback);
total_bytes += itch_data.size();
}
// Report throughput in MB/s
state.SetBytesProcessed(static_cast<int64_t>(total_bytes));
}
BENCHMARK_F(ParserBenchmark, BM_ParseAndCollectAll)(benchmark::State& state) {
size_t total_bytes = 0;
for ([[maybe_unused]] auto iter : state) {
auto messages = parser.parse(itch_data.data(), itch_data.size());
benchmark::DoNotOptimize(messages.data());
total_bytes += itch_data.size();
}
state.SetBytesProcessed(static_cast<int64_t>(total_bytes));
}
BENCHMARK_F(ParserBenchmark, BM_ParseAndFilter)(benchmark::State& state) {
size_t total_bytes = 0;
for ([[maybe_unused]] auto iter : state) {
auto messages = parser.parse(itch_data.data(), itch_data.size(), {'A', 'P', 'E', 'C', 'X'});
benchmark::DoNotOptimize(messages.data());
total_bytes += itch_data.size();
}
state.SetBytesProcessed(static_cast<int64_t>(total_bytes));
}
int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: ./parser_bench <path_to_itch_data_file> [google "
"benchmark options]\n";
return 1;
}
data::g_data_filename = argv[1];
// Manually remove the filename argument from the list before passing to
// benchmark::Initialize. We do this by shifting all subsequent arguments
// one position to the left.
for (int i = 1; i < argc - 1; ++i) {
argv[i] = argv[i + 1];
}
// Decrement the argument count to reflect the removal of our custom
// argument.
argc--;
// Initialize Google Benchmark, passing it the remaining arguments.
// Note: We "consume" the first two arguments (executable name and filename)
// so Google Benchmark doesn't try to parse them.
benchmark::Initialize(&argc, argv);
// Check if any benchmarks are targeted to run. If not, we might want to
// return.
if (benchmark::ReportUnrecognizedArguments(argc, argv)) {
return 1;
};
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
return 0;
}