6986eef7
Add BaselineRecord + JSON round-trip for frame stats
a73x 2026-04-17 15:28
Commit message
src/bench_stats.zig
| Old | New | ||
|---|---|---|---|
| @@ -200,3 +200,68 @@ test "FrameTimingStats handles empty ring" { | |||
| 200 | try std.testing.expectEqual(@as(usize, 0), stats.frame_count); | 200 | try std.testing.expectEqual(@as(usize, 0), stats.frame_count); |
| 201 | try std.testing.expectEqual(@as(u32, 0), stats.snapshot.min); | 201 | try std.testing.expectEqual(@as(u32, 0), stats.snapshot.min); |
| 202 | } | 202 | } |
| 203 | |||
| 204 | pub const BaselineRecord = struct { | ||
| 205 | workload_sha: []const u8, | ||
| 206 | zig_version: []const u8, | ||
| 207 | waystty_sha: []const u8, | ||
| 208 | frame_count: usize, | ||
| 209 | sections: struct { | ||
| 210 | snapshot: SectionStats, | ||
| 211 | row_rebuild: SectionStats, | ||
| 212 | atlas_upload: SectionStats, | ||
| 213 | instance_upload: SectionStats, | ||
| 214 | gpu_submit: SectionStats, | ||
| 215 | }, | ||
| 216 | }; | ||
| 217 | |||
| 218 | /// Serialize `rec` to JSON and return an owned slice. Caller must free. | ||
| 219 | pub fn writeBaselineJson(alloc: std.mem.Allocator, rec: BaselineRecord) ![]u8 { | ||
| 220 | var out: std.Io.Writer.Allocating = .init(alloc); | ||
| 221 | errdefer out.deinit(); | ||
| 222 | try std.json.Stringify.value(rec, .{ .whitespace = .indent_2 }, &out.writer); | ||
| 223 | return out.toOwnedSlice(); | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn readBaselineJson(alloc: std.mem.Allocator, bytes: []const u8) !BaselineRecord { | ||
| 227 | var parsed = try std.json.parseFromSlice(BaselineRecord, alloc, bytes, .{}); | ||
| 228 | defer parsed.deinit(); | ||
| 229 | return .{ | ||
| 230 | .workload_sha = try alloc.dupe(u8, parsed.value.workload_sha), | ||
| 231 | .zig_version = try alloc.dupe(u8, parsed.value.zig_version), | ||
| 232 | .waystty_sha = try alloc.dupe(u8, parsed.value.waystty_sha), | ||
| 233 | .frame_count = parsed.value.frame_count, | ||
| 234 | .sections = parsed.value.sections, | ||
| 235 | }; | ||
| 236 | } | ||
| 237 | |||
| 238 | test "baseline JSON round-trip" { | ||
| 239 | const alloc = std.testing.allocator; | ||
| 240 | const rec = BaselineRecord{ | ||
| 241 | .workload_sha = "abcdef", | ||
| 242 | .zig_version = "0.15.0", | ||
| 243 | .waystty_sha = "123abc", | ||
| 244 | .frame_count = 256, | ||
| 245 | .sections = .{ | ||
| 246 | .snapshot = .{ .min = 1, .avg = 2, .p99 = 3, .max = 4 }, | ||
| 247 | .row_rebuild = .{ .min = 10, .avg = 20, .p99 = 30, .max = 40 }, | ||
| 248 | .atlas_upload = .{ .min = 0, .avg = 0, .p99 = 0, .max = 0 }, | ||
| 249 | .instance_upload = .{ .min = 5, .avg = 6, .p99 = 7, .max = 8 }, | ||
| 250 | .gpu_submit = .{ .min = 9, .avg = 9, .p99 = 9, .max = 9 }, | ||
| 251 | }, | ||
| 252 | }; | ||
| 253 | |||
| 254 | const json_bytes = try writeBaselineJson(alloc, rec); | ||
| 255 | defer alloc.free(json_bytes); | ||
| 256 | |||
| 257 | const parsed = try readBaselineJson(alloc, json_bytes); | ||
| 258 | defer { | ||
| 259 | alloc.free(parsed.workload_sha); | ||
| 260 | alloc.free(parsed.zig_version); | ||
| 261 | alloc.free(parsed.waystty_sha); | ||
| 262 | } | ||
| 263 | |||
| 264 | try std.testing.expectEqual(@as(usize, 256), parsed.frame_count); | ||
| 265 | try std.testing.expectEqual(@as(u32, 30), parsed.sections.row_rebuild.p99); | ||
| 266 | try std.testing.expectEqualStrings("abcdef", parsed.workload_sha); | ||
| 267 | } | ||