lib/trace/src/profiling/storage.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const bench = @import("bench");
3 const coz = @import("coz");
4 const trace = @import("trace");
5
6 pub const scales = [_]usize{ 1_000, 10_000, 100_000 };
7
8 pub const State = struct {
9 write_path: []const u8,
10 read_paths: [scales.len][]const u8,
11 };
12
13 var state: ?State = null;
14
15 pub fn install(new_state: State) void {
16 state = new_state;
17 }
18
19 pub fn reset() void {
20 state = null;
21 }
22
23 pub fn buildFixture(allocator: std.mem.Allocator, path: []const u8, count: usize) !void {
24 const manifest = trace.Manifest{ .build_id = "trace-storage-bench" };
25 const limits = trace.TraceLimits{};
26 var writer_storage = try trace.TraceWriterStorage.init(
27 allocator,
28 trace.TraceWriterLimits.fromOpen(path, manifest, limits),
29 );
30 defer writer_storage.deinit(allocator);
31 writer_storage.activate();
32 var trace_writer: trace.TraceWriter = .{};
33 try trace_writer.open(&writer_storage, path, manifest, limits);
34 defer trace_writer.deinit();
35 for (0..count) |index| try trace_writer.append(workloadEvent(index));
36 try trace_writer.finish();
37 }
38
39 pub fn writeSmall(allocator: std.mem.Allocator) void {
40 writeScale(allocator, scales[0]);
41 }
42
43 pub fn writeMedium(allocator: std.mem.Allocator) void {
44 writeScale(allocator, scales[1]);
45 }
46
47 pub fn writeLarge(allocator: std.mem.Allocator) void {
48 writeScale(allocator, scales[2]);
49 }
50
51 pub fn verifySmall(allocator: std.mem.Allocator) void {
52 verifyScale(allocator, 0);
53 }
54
55 pub fn verifyMedium(allocator: std.mem.Allocator) void {
56 verifyScale(allocator, 1);
57 }
58
59 pub fn verifyLarge(allocator: std.mem.Allocator) void {
60 verifyScale(allocator, 2);
61 }
62
63 fn writeScale(allocator: std.mem.Allocator, count: usize) void {
64 const installed = state orelse @panic("trace storage benchmark state is not installed");
65 const manifest = trace.Manifest{ .build_id = "trace-storage-bench" };
66 const limits = trace.TraceLimits{};
67 var writer_storage = trace.TraceWriterStorage.init(
68 allocator,
69 trace.TraceWriterLimits.fromOpen(installed.write_path, manifest, limits),
70 ) catch @panic("trace storage benchmark storage failed to initialize");
71 defer writer_storage.deinit(allocator);
72 writer_storage.activate();
73 var trace_writer: trace.TraceWriter = .{};
74 trace_writer.open(&writer_storage, installed.write_path, manifest, limits) catch
75 @panic("trace storage benchmark writer failed to open");
76 defer trace_writer.deinit();
77 const phase = bench.phaseAt("trace.storage.write", @src());
78 defer phase.end();
79 for (0..count) |index| trace_writer.append(workloadEvent(index)) catch
80 @panic("trace storage benchmark append failed");
81 trace_writer.finish() catch @panic("trace storage benchmark writer failed to finish");
82 std.mem.doNotOptimizeAway(count);
83 coz.progressNamed("trace.storage.write.complete");
84 }
85
86 fn verifyScale(allocator: std.mem.Allocator, scale_index: usize) void {
87 const installed = state orelse @panic("trace storage benchmark state is not installed");
88 const path = installed.read_paths[scale_index];
89 var reader_storage = trace.TraceReaderStorage.init(
90 allocator,
91 trace.TraceReaderLimits.fromOpen(path, .{}),
92 ) catch @panic("trace storage benchmark reader storage failed to initialize");
93 defer reader_storage.deinit(allocator);
94 reader_storage.activate();
95 var trace_reader: trace.TraceReader = .{};
96 trace_reader.open(&reader_storage, path) catch
97 @panic("trace storage benchmark reader failed to open");
98 defer trace_reader.deinit();
99 const phase = bench.phaseAt("trace.storage.verify", @src());
100 defer phase.end();
101 const result = trace_reader.verify() catch @panic("trace storage benchmark verification failed");
102 if (result.event_count != scales[scale_index]) @panic("trace storage benchmark witness failed");
103 std.mem.doNotOptimizeAway(result.checksum);
104 coz.progressNamed("trace.storage.verify.complete");
105 }
106
107 fn workloadEvent(index: usize) trace.Event {
108 return trace.Event.user(
109 .{
110 .thread_id = @intCast(index % 16 + 1),
111 .seq = index + 1,
112 },
113 switch (index % 4) {
114 0 => "compiler.query.start",
115 1 => "compiler.query.cache",
116 2 => "compiler.query.finish",
117 else => "compiler.query.publish",
118 },
119 switch (index % 3) {
120 0 => "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"function_summary\",\"elapsed_ns\":1700,\"cache\":\"miss\"}",
121 1 => "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"partial_evaluation\",\"elapsed_ns\":2900,\"cache\":\"hit\"}",
122 else => "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"source_choir_module\",\"elapsed_ns\":4300,\"cache\":\"hit\"}",
123 },
124 );
125 }
126
127 test "storage benchmark fixture rolls over and verifies" {
128 const allocator = std.testing.allocator;
129 var tmp = std.testing.tmpDir(.{});
130 defer tmp.cleanup();
131 const root = try tmp.parent_dir.realPathFileAlloc(@import("sys").fs.debugIo(), tmp.sub_path[0..], allocator);
132 defer allocator.free(root);
133 const path = try std.fs.path.join(allocator, &.{ root, "fixture.trace" });
134 defer allocator.free(path);
135 try buildFixture(allocator, path, 20_000);
136 var reader_storage = try trace.TraceReaderStorage.init(
137 allocator,
138 trace.TraceReaderLimits.fromOpen(path, .{}),
139 );
140 defer reader_storage.deinit(allocator);
141 reader_storage.activate();
142 const result = try trace.store.verify(&reader_storage, path);
143 try std.testing.expectEqual(@as(u64, 20_000), result.event_count);
144 try std.testing.expect(result.chunk_count > 1);
145 }