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 }