lib/bench/src/compare/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const storage_alignment: usize = @max(@alignOf(f64), @alignOf(u64));
4
5 pub const Limits = struct {
6 max_samples_per_series: usize,
7 max_bootstrap_iterations: u32,
8 };
9
10 pub const DeriveError = error{
11 InvalidSampleLimit,
12 SampleLimitTooLarge,
13 InvalidBootstrapIterationLimit,
14 CapacityOverflow,
15 };
16
17 pub const Capacity = struct {
18 limits: Limits,
19 effects_offset: usize,
20 samples_offset: usize,
21 counts_offset: usize,
22 storage_bytes: usize,
23
24 pub fn derive(limits: Limits) DeriveError!Capacity {
25 if (limits.max_samples_per_series == 0) return error.InvalidSampleLimit;
26 if (limits.max_samples_per_series > std.math.maxInt(u32)) {
27 return error.SampleLimitTooLarge;
28 }
29 if (limits.max_bootstrap_iterations == 0) {
30 return error.InvalidBootstrapIterationLimit;
31 }
32 const effects = try placed(f64, 0, limits.max_bootstrap_iterations);
33 const samples = try placed(u64, effects.end, limits.max_samples_per_series);
34 const counts = try placed(u32, samples.end, limits.max_samples_per_series);
35 std.debug.assert(effects.start == 0);
36 std.debug.assert(effects.end <= samples.start);
37 std.debug.assert(samples.end <= counts.start);
38 return .{
39 .limits = limits,
40 .effects_offset = effects.start,
41 .samples_offset = samples.start,
42 .counts_offset = counts.start,
43 .storage_bytes = counts.end,
44 };
45 }
46 };
47
48 const Region = struct {
49 start: usize,
50 end: usize,
51 };
52
53 fn placed(comptime T: type, offset: usize, count: usize) DeriveError!Region {
54 const mask: usize = @alignOf(T) - 1;
55 const padded = std.math.add(usize, offset, mask) catch return error.CapacityOverflow;
56 const start = padded & ~mask;
57 const bytes = std.math.mul(usize, count, @sizeOf(T)) catch return error.CapacityOverflow;
58 return .{
59 .start = start,
60 .end = std.math.add(usize, start, bytes) catch return error.CapacityOverflow,
61 };
62 }
63
64 fn modelCapacity(limits: Limits) DeriveError!Capacity {
65 if (limits.max_samples_per_series == 0) return error.InvalidSampleLimit;
66 if (limits.max_samples_per_series > std.math.maxInt(u32)) {
67 return error.SampleLimitTooLarge;
68 }
69 if (limits.max_bootstrap_iterations == 0) {
70 return error.InvalidBootstrapIterationLimit;
71 }
72 const iterations: u128 = limits.max_bootstrap_iterations;
73 const samples: u128 = limits.max_samples_per_series;
74 const effects_offset: u128 = 0;
75 const samples_offset = effects_offset + iterations * @sizeOf(f64);
76 const counts_offset = samples_offset + samples * @sizeOf(u64);
77 const storage_bytes = counts_offset + samples * @sizeOf(u32);
78 if (samples_offset > std.math.maxInt(usize) or
79 counts_offset > std.math.maxInt(usize) or
80 storage_bytes > std.math.maxInt(usize))
81 {
82 return error.CapacityOverflow;
83 }
84 return .{
85 .limits = limits,
86 .effects_offset = @intCast(effects_offset),
87 .samples_offset = @intCast(samples_offset),
88 .counts_offset = @intCast(counts_offset),
89 .storage_bytes = @intCast(storage_bytes),
90 };
91 }
92
93 test "comparison capacity matches an independent byte model" {
94 comptime {
95 @stardustClaim(
96 @import("alloc_phase").capacity.witness(@import("./root.zig").ComparisonStorage, "bench_comparison_capacity"),
97 null,
98 null,
99 null,
100 null,
101 null,
102 null,
103 );
104 }
105
106 const limits = Limits{
107 .max_samples_per_series = 8,
108 .max_bootstrap_iterations = 500,
109 };
110 try std.testing.expectEqual(try modelCapacity(limits), try Capacity.derive(limits));
111 try std.testing.expectEqual(@as(usize, 4_096), (try Capacity.derive(limits)).storage_bytes);
112 }
113
114 test "comparison capacity rejects invalid and overflowing limits" {
115 try std.testing.expectError(
116 error.InvalidSampleLimit,
117 Capacity.derive(.{ .max_samples_per_series = 0, .max_bootstrap_iterations = 1 }),
118 );
119 try std.testing.expectError(
120 error.InvalidBootstrapIterationLimit,
121 Capacity.derive(.{ .max_samples_per_series = 1, .max_bootstrap_iterations = 0 }),
122 );
123 const extreme_error = if (@bitSizeOf(usize) > @bitSizeOf(u32))
124 error.SampleLimitTooLarge
125 else
126 error.CapacityOverflow;
127 try std.testing.expectError(extreme_error, Capacity.derive(.{
128 .max_samples_per_series = std.math.maxInt(usize),
129 .max_bootstrap_iterations = 1,
130 }));
131 }