Skip to content

Commit e78261d

Browse files
committed
Add the ability to append to the result json file for wallclock benchmarks (AI Generated)
1 parent 57faaec commit e78261d

1 file changed

Lines changed: 136 additions & 30 deletions

File tree

core/src/walltime.cpp

Lines changed: 136 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,8 @@ static std::string escape_backslashes(const std::string &input) {
5959
return output;
6060
}
6161

62-
static void write_codspeed_benchmarks_to_json(
63-
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
62+
static std::string serialize_benchmark_objects(const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
6463
std::ostringstream oss;
65-
66-
std::string creator_name = "codspeed-cpp";
67-
std::string creator_version = CODSPEED_VERSION;
68-
#ifdef _WIN32
69-
auto creator_pid = _getpid();
70-
#else
71-
pid_t creator_pid = getpid();
72-
#endif
73-
std::string instrument_type = "walltime";
74-
75-
oss << "{\n";
76-
oss << " \"creator\": {\n";
77-
oss << " \"name\": \"" << creator_name << "\",\n";
78-
oss << " \"version\": \"" << creator_version << "\",\n";
79-
oss << " \"pid\": " << creator_pid << "\n";
80-
oss << " },\n";
81-
oss << " \"instrument\": {\n";
82-
oss << " \"type\": \"" << instrument_type << "\"\n";
83-
oss << " },\n";
84-
oss << " \"benchmarks\": [\n";
85-
8664
for (size_t i = 0; i < benchmarks.size(); ++i) {
8765
const auto &benchmark = benchmarks[i];
8866
const auto &stats = benchmark.stats;
@@ -122,9 +100,16 @@ static void write_codspeed_benchmarks_to_json(
122100
}
123101
oss << "\n";
124102
}
103+
return oss.str();
104+
}
125105

126-
oss << " ]\n";
127-
oss << "}";
106+
// Extracts the file path calculation and directory creation to avoid redundancy
107+
static std::string get_codspeed_results_file_path() {
108+
#ifdef _WIN32
109+
auto creator_pid = _getpid();
110+
#else
111+
pid_t creator_pid = getpid();
112+
#endif
128113

129114
// Determine the directory path
130115
std::string profile_folder = safe_getenv("CODSPEED_PROFILE_FOLDER");
@@ -135,25 +120,146 @@ static void write_codspeed_benchmarks_to_json(
135120
if (!std::filesystem::exists(results_path)) {
136121
if (!std::filesystem::create_directories(results_path)) {
137122
std::cerr << "Failed to create directory: " << results_path << std::endl;
138-
return;
123+
return "";
139124
}
140125
}
141126

142127
// Create the file path
143128
std::ostringstream file_path;
144129
file_path << results_path.string() << "/" << creator_pid << ".json";
130+
return file_path.str();
131+
}
132+
133+
static void write_codspeed_benchmarks_to_json(
134+
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
135+
std::ostringstream oss;
136+
137+
std::string creator_name = "codspeed-cpp";
138+
std::string creator_version = CODSPEED_VERSION;
139+
#ifdef _WIN32
140+
auto creator_pid = _getpid();
141+
#else
142+
pid_t creator_pid = getpid();
143+
#endif
144+
std::string instrument_type = "walltime";
145+
146+
oss << "{\n";
147+
oss << " \"creator\": {\n";
148+
oss << " \"name\": \"" << creator_name << "\",\n";
149+
oss << " \"version\": \"" << creator_version << "\",\n";
150+
oss << " \"pid\": " << creator_pid << "\n";
151+
oss << " },\n";
152+
oss << " \"instrument\": {\n";
153+
oss << " \"type\": \"" << instrument_type << "\"\n";
154+
oss << " },\n";
155+
oss << " \"benchmarks\": [\n";
156+
157+
oss << serialize_benchmark_objects(benchmarks);
158+
159+
oss << " ]\n";
160+
oss << "}";
161+
162+
std::string file_path_str = get_codspeed_results_file_path();
163+
if (file_path_str.empty()) return;
145164

146165
// Write to file
147-
std::ofstream out_file(file_path.str());
166+
std::ofstream out_file(file_path_str);
148167
if (out_file.is_open()) {
149168
out_file << oss.str();
150169
out_file.close();
151-
std::cout << "JSON written to " << file_path.str() << std::endl;
170+
std::cout << "JSON written to " << file_path_str << std::endl;
152171
} else {
153-
std::cerr << "Unable to open file " << file_path.str() << std::endl;
172+
std::cerr << "Unable to open file " << file_path_str << std::endl;
154173
}
155174
}
156175

176+
static void append_codspeed_benchmarks_to_json(
177+
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
178+
179+
if (benchmarks.empty()) return;
180+
181+
std::string file_path_str = get_codspeed_results_file_path();
182+
if (file_path_str.empty()) return;
183+
184+
if (!std::filesystem::exists(file_path_str) || std::filesystem::file_size(file_path_str) == 0) {
185+
write_codspeed_benchmarks_to_json(benchmarks);
186+
return;
187+
}
188+
189+
std::fstream file(file_path_str, std::ios::in | std::ios::out | std::ios::binary);
190+
if (!file) {
191+
std::cerr << "Failed to open file for appending: " << file_path_str << std::endl;
192+
return;
193+
}
194+
195+
file.seekp(0, std::ios::end);
196+
std::streampos pos = file.tellp();
197+
198+
int state = 0;
199+
bool array_was_empty = false;
200+
std::streampos truncation_pos = 0;
201+
202+
while (pos > 0) {
203+
pos -= 1;
204+
file.seekg(pos);
205+
char ch;
206+
file.get(ch);
207+
208+
// State 0: Skip trailing whitespace outside the root object, find '}'
209+
if (state == 0) {
210+
if (std::isspace(static_cast<unsigned char>(ch))) continue;
211+
if (ch == '}') {
212+
state = 1;
213+
continue;
214+
}
215+
}
216+
217+
// State 1: Skip whitespace between '}' and ']', find ']'
218+
if (state == 1) {
219+
if (std::isspace(static_cast<unsigned char>(ch))) continue;
220+
if (ch == ']') {
221+
state = 2;
222+
continue;
223+
}
224+
}
225+
226+
// State 2: We are inside the array. Skip whitespace to find the actual content end
227+
if (state == 2) {
228+
if (std::isspace(static_cast<unsigned char>(ch))) {
229+
continue;
230+
}
231+
232+
if (ch == '[') {
233+
array_was_empty = true;
234+
}
235+
236+
truncation_pos = pos + std::streamoff(1);
237+
break;
238+
}
239+
}
240+
241+
if (state < 2) {
242+
std::cerr << "Failed to find valid JSON structural endings to safely append." << std::endl;
243+
return;
244+
}
245+
246+
file.seekp(truncation_pos);
247+
248+
if (!array_was_empty) {
249+
file << ",\n";
250+
} else {
251+
file << "\n";
252+
}
253+
254+
// Inject the new items
255+
file << serialize_benchmark_objects(benchmarks);
256+
257+
file << " ]\n}";
258+
259+
file.flush();
260+
std::filesystem::resize_file(file_path_str, file.tellp());
261+
}
262+
157263
BenchmarkStats compute_benchmark_stats(
158264
const RawWalltimeBenchmark &raw_benchmark) {
159265
assert(raw_benchmark.iters_per_round.size() ==
@@ -245,7 +351,7 @@ void generate_codspeed_walltime_report(
245351
codspeed_walltime_benchmarks.push_back(codspeed_benchmark);
246352
}
247353

248-
write_codspeed_benchmarks_to_json(codspeed_walltime_benchmarks);
354+
append_codspeed_benchmarks_to_json(codspeed_walltime_benchmarks);
249355
}
250356

251357
} // namespace codspeed

0 commit comments

Comments
 (0)