|
| 1 | +const std = @import("std"); |
| 2 | +const time = std.time; |
| 3 | + |
| 4 | +const BenchmarkResult = struct { |
| 5 | + name: []const u8, |
| 6 | + iterations: u64, |
| 7 | + total_ns: u64, |
| 8 | + avg_ns: u64, |
| 9 | + min_ns: u64, |
| 10 | + max_ns: u64, |
| 11 | + ops_per_sec: f64, |
| 12 | +}; |
| 13 | + |
| 14 | +const BenchmarkSuite = struct { |
| 15 | + results: std.ArrayList(BenchmarkResult), |
| 16 | + allocator: std.mem.Allocator, |
| 17 | + |
| 18 | + pub fn init(allocator: std.mem.Allocator) BenchmarkSuite { |
| 19 | + return .{ |
| 20 | + .results = std.ArrayList(BenchmarkResult).init(allocator), |
| 21 | + .allocator = allocator, |
| 22 | + }; |
| 23 | + } |
| 24 | + |
| 25 | + pub fn deinit(self: *BenchmarkSuite) void { |
| 26 | + self.results.deinit(); |
| 27 | + } |
| 28 | + |
| 29 | + pub fn run(self: *BenchmarkSuite, comptime name: []const u8, comptime func: fn (allocator: std.mem.Allocator) void, iterations: u64) !void { |
| 30 | + const allocator = self.allocator; |
| 31 | + |
| 32 | + var min_ns: u64 = std.math.maxInt(u64); |
| 33 | + var max_ns: u64 = 0; |
| 34 | + var total_ns: u64 = 0; |
| 35 | + |
| 36 | + var i: u64 = 0; |
| 37 | + while (i < iterations) : (i += 1) { |
| 38 | + const start = time.nanoTimestamp(); |
| 39 | + func(allocator); |
| 40 | + const elapsed = @as(u64, @intCast(time.nanoTimestamp() - start)); |
| 41 | + |
| 42 | + if (elapsed < min_ns) min_ns = elapsed; |
| 43 | + if (elapsed > max_ns) max_ns = elapsed; |
| 44 | + total_ns += elapsed; |
| 45 | + } |
| 46 | + |
| 47 | + const avg_ns = total_ns / iterations; |
| 48 | + const ops_per_sec = @as(f64, @floatFromInt(iterations)) / (@as(f64, @floatFromInt(total_ns)) / 1e9); |
| 49 | + |
| 50 | + const result = BenchmarkResult{ |
| 51 | + .name = name, |
| 52 | + .iterations = iterations, |
| 53 | + .total_ns = total_ns, |
| 54 | + .avg_ns = avg_ns, |
| 55 | + .min_ns = min_ns, |
| 56 | + .max_ns = max_ns, |
| 57 | + .ops_per_sec = ops_per_sec, |
| 58 | + }; |
| 59 | + |
| 60 | + try self.results.append(result); |
| 61 | + } |
| 62 | + |
| 63 | + pub fn printReport(self: *BenchmarkSuite) void { |
| 64 | + const stdout = std.io.getStdOut().writer(); |
| 65 | + |
| 66 | + stdout.print("\n{s}\n", .{"=" ** 72}) catch {}; |
| 67 | + stdout.print(" TRINITY Benchmark Suite — Results\n", .{}); |
| 68 | + stdout.print("{s}\n\n", .{"=" * 72}) catch {}; |
| 69 | + |
| 70 | + stdout.print(" {s:<30} {s:>10} {s:>10} {s:>10} {s:>12}\n", .{ "Benchmark", "Avg (ns)", "Min (ns)", "Max (ns)", "ops/sec" }); |
| 71 | + stdout.print(" {s}\n", .{"-" * 72}) catch {}; |
| 72 | + |
| 73 | + for (self.results.items) |r| { |
| 74 | + stdout.print(" {s:<30} {d:>10} {d:>10} {d:>10} {d:>12.1}\n", .{ |
| 75 | + r.name, |
| 76 | + r.avg_ns, |
| 77 | + r.min_ns, |
| 78 | + r.max_ns, |
| 79 | + r.ops_per_sec, |
| 80 | + }) catch {}; |
| 81 | + } |
| 82 | + |
| 83 | + stdout.print("\n{s}\n", .{"=" * 72}) catch {}; |
| 84 | + } |
| 85 | + |
| 86 | + pub fn exportJson(self: *BenchmarkSuite) ![]const u8 { |
| 87 | + var buf = std.ArrayList(u8).init(self.allocator); |
| 88 | + try buf.appendSlice("{\"benchmarks\":[\n"); |
| 89 | + |
| 90 | + for (self.results.items, 0..) |r, i| { |
| 91 | + if (i > 0) try buf.appendSlice(",\n"); |
| 92 | + try std.fmt.format(buf.writer(), |
| 93 | + \\ {{"name":"{s}","iterations":{d},"total_ns":{d},"avg_ns":{d},"min_ns":{d},"max_ns":{d},"ops_per_sec":{d:.1}}} |
| 94 | + , .{ r.name, r.iterations, r.total_ns, r.avg_ns, r.min_ns, r.max_ns, r.ops_per_sec }); |
| 95 | + } |
| 96 | + |
| 97 | + try buf.appendSlice("\n]}\n"); |
| 98 | + return buf.toOwnedSlice(); |
| 99 | + } |
| 100 | +}; |
| 101 | + |
| 102 | +fn benchTernaryEncode(allocator: std.mem.Allocator) void { |
| 103 | + var values = [_]f64{ 0.0, 1.0, -1.0, 0.5, -0.5, 1.618, -1.618, 3.14159 }; |
| 104 | + var encoded = [_]u8{0} ** 8; |
| 105 | + for (&values, 0..) |v, i| { |
| 106 | + encoded[i] = if (v > 0.33) @as(u8, 1) else if (v < -0.33) @as(u8, 255) else @as(u8, 0); |
| 107 | + } |
| 108 | + _ = allocator; |
| 109 | + _ = encoded; |
| 110 | +} |
| 111 | + |
| 112 | +fn benchTernaryMatmul(allocator: std.mem.Allocator) void { |
| 113 | + const N: usize = 64; |
| 114 | + var a: [N]f64 = undefined; |
| 115 | + var b: [N]f64 = undefined; |
| 116 | + var c: [N]f64 = undefined; |
| 117 | + |
| 118 | + for (&a, 0..) |*v, i| v.* = @floatFromInt(i % 3 - 1); |
| 119 | + for (&b, 0..) |*v, i| v.* = @floatFromInt((i + 1) % 3 - 1); |
| 120 | + |
| 121 | + for (&c, 0..) |*v, i| { |
| 122 | + var sum: f64 = 0; |
| 123 | + var j: usize = 0; |
| 124 | + while (j < N) : (j += 1) { |
| 125 | + sum += a[j] * b[(i + j) % N]; |
| 126 | + } |
| 127 | + v.* = sum; |
| 128 | + } |
| 129 | + _ = allocator; |
| 130 | +} |
| 131 | + |
| 132 | +fn benchVsaBind(allocator: std.mem.Allocator) void { |
| 133 | + const dim = 1024; |
| 134 | + var a: [dim]f64 = undefined; |
| 135 | + var b: [dim]f64 = undefined; |
| 136 | + var result: [dim]f64 = undefined; |
| 137 | + |
| 138 | + for (&a, 0..) |*v, i| v.* = if (i % 2 == 0) 1.0 else -1.0; |
| 139 | + for (&b, 0..) |*v, i| v.* = if (i % 3 == 0) 1.0 else -1.0; |
| 140 | + |
| 141 | + for (&result, 0..) |*v, i| v.* = a[i] * b[i]; |
| 142 | + _ = allocator; |
| 143 | +} |
| 144 | + |
| 145 | +fn benchVsaBundle(allocator: std.mem.Allocator) void { |
| 146 | + const dim = 1024; |
| 147 | + const n_vecs = 10; |
| 148 | + var vecs: [n_vecs][dim]f64 = undefined; |
| 149 | + var result: [dim]f64 = undefined; |
| 150 | + |
| 151 | + for (&vecs, 0..) |*vec, vi| { |
| 152 | + for (vec, 0..) |*v, i| { |
| 153 | + v.* = if ((vi + i) % 2 == 0) 1.0 else -1.0; |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + for (&result, 0..) |*v, i| { |
| 158 | + var sum: f64 = 0; |
| 159 | + for (&vecs, 0..) |*vec, _| { |
| 160 | + sum += vec[i]; |
| 161 | + } |
| 162 | + v.* = sum / @as(f64, @floatFromInt(n_vecs)); |
| 163 | + } |
| 164 | + _ = allocator; |
| 165 | +} |
| 166 | + |
| 167 | +fn benchVsaCosine(allocator: std.mem.Allocator) void { |
| 168 | + const dim = 1024; |
| 169 | + var a: [dim]f64 = undefined; |
| 170 | + var b: [dim]f64 = undefined; |
| 171 | + |
| 172 | + for (&a, 0..) |*v, i| v.* = if (i % 2 == 0) 1.0 else -1.0; |
| 173 | + for (&b, 0..) |*v, i| v.* = if (i % 3 == 0) 1.0 else -1.0; |
| 174 | + |
| 175 | + var dot: f64 = 0; |
| 176 | + var norm_a: f64 = 0; |
| 177 | + var norm_b: f64 = 0; |
| 178 | + for (&a, 0..) |_, i| { |
| 179 | + dot += a[i] * b[i]; |
| 180 | + norm_a += a[i] * a[i]; |
| 181 | + norm_b += b[i] * b[i]; |
| 182 | + } |
| 183 | + const cos_sim = dot / (@sqrt(norm_a) * @sqrt(norm_b)); |
| 184 | + _ = cos_sim; |
| 185 | + _ = allocator; |
| 186 | +} |
| 187 | + |
| 188 | +fn benchGf16Encode(allocator: std.mem.Allocator) void { |
| 189 | + const n = 1024; |
| 190 | + var values: [n]f64 = undefined; |
| 191 | + var encoded: [n]u16 = undefined; |
| 192 | + |
| 193 | + for (&values, 0..) |*v, i| v.* = @as(f64, @floatFromInt(i)) * 0.1; |
| 194 | + |
| 195 | + for (&encoded, 0..) |*v, i| { |
| 196 | + const fval = values[i]; |
| 197 | + const sign: u16 = if (fval < 0) 1 << 15 else 0; |
| 198 | + const abs_val = @abs(fval); |
| 199 | + const exp_val: u16 = if (abs_val > 0) @intFromFloat(@log2(abs_val)) + 31 else 0; |
| 200 | + const mantissa: u16 = @intFromFloat(@rem(abs_val, 1.0) * 512.0); |
| 201 | + v.* = sign | (exp_val << 9) | (mantissa & 0x1FF); |
| 202 | + } |
| 203 | + _ = allocator; |
| 204 | +} |
| 205 | + |
| 206 | +fn benchGf16Decode(allocator: std.mem.Allocator) void { |
| 207 | + const n = 1024; |
| 208 | + var encoded: [n]u16 = undefined; |
| 209 | + var decoded: [n]f64 = undefined; |
| 210 | + |
| 211 | + for (&encoded, 0..) |*v, i| v.* = @intCast(i % 65536); |
| 212 | + |
| 213 | + for (&decoded, 0..) |*v, i| { |
| 214 | + const raw = encoded[i]; |
| 215 | + const sign: f64 = if ((raw >> 15) & 1 == 1) -1.0 else 1.0; |
| 216 | + const exp: u16 = (raw >> 9) & 0x3F; |
| 217 | + const mant: u16 = raw & 0x1FF; |
| 218 | + v.* = sign * @as(f64, @floatFromInt(@as(u32, 1) << @intCast(exp))) * (1.0 + @as(f64, @floatFromInt(mant)) / 512.0); |
| 219 | + } |
| 220 | + _ = allocator; |
| 221 | +} |
| 222 | + |
| 223 | +fn benchPhiComputation(allocator: std.mem.Allocator) void { |
| 224 | + const phi = (1.0 + @sqrt(5.0)) / 2.0; |
| 225 | + var result: f64 = 0; |
| 226 | + var i: u64 = 0; |
| 227 | + while (i < 1000) : (i += 1) { |
| 228 | + result += phi * phi + 1.0 / (phi * phi); |
| 229 | + } |
| 230 | + result /= 1000.0; |
| 231 | + _ = result; |
| 232 | + _ = allocator; |
| 233 | +} |
| 234 | + |
| 235 | +pub fn main() !void { |
| 236 | + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
| 237 | + defer _ = gpa.deinit(); |
| 238 | + const allocator = gpa.allocator(); |
| 239 | + |
| 240 | + var suite = BenchmarkSuite.init(allocator); |
| 241 | + defer suite.deinit(); |
| 242 | + |
| 243 | + const stdout = std.io.getStdOut().writer(); |
| 244 | + stdout.print("\n{s}\n", .{"=" * 72}) catch {}; |
| 245 | + stdout.print(" TRINITY S3AI — Reproducible Benchmark Suite\n", .{}); |
| 246 | + stdout.print(" phi^2 + 1/phi^2 = 3 | TRINITY\n", .{}); |
| 247 | + stdout.print("{s}\n\n", .{"=" * 72}) catch {}; |
| 248 | + |
| 249 | + stdout.print(" Running benchmarks...\n\n", .{}); |
| 250 | + |
| 251 | + try suite.run("ternary_encode", benchTernaryEncode, 10000); |
| 252 | + try suite.run("ternary_matmul_64", benchTernaryMatmul, 10000); |
| 253 | + try suite.run("vsa_bind_1024", benchVsaBind, 10000); |
| 254 | + try suite.run("vsa_bundle_10x1024", benchVsaBundle, 10000); |
| 255 | + try suite.run("vsa_cosine_1024", benchVsaCosine, 10000); |
| 256 | + try suite.run("gf16_encode", benchGf16Encode, 10000); |
| 257 | + try suite.run("gf16_decode", benchGf16Decode, 10000); |
| 258 | + try suite.run("phi_computation", benchPhiComputation, 10000); |
| 259 | + |
| 260 | + suite.printReport(); |
| 261 | + |
| 262 | + const json = try suite.exportJson(); |
| 263 | + defer allocator.free(json); |
| 264 | + |
| 265 | + const json_file = try std.fs.cwd().createFile("benchmarks/results.json", .{}); |
| 266 | + defer json_file.close(); |
| 267 | + try json_file.writeAll(json); |
| 268 | + |
| 269 | + stdout.print(" Results exported to benchmarks/results.json\n", .{}); |
| 270 | +} |
| 271 | + |
| 272 | +test "benchmark suite runs" { |
| 273 | + var suite = BenchmarkSuite.init(std.testing.allocator); |
| 274 | + defer suite.deinit(); |
| 275 | + |
| 276 | + try suite.run("test_bench", benchPhiComputation, 100); |
| 277 | + try std.testing.expect(suite.results.items.len == 1); |
| 278 | + try std.testing.expect(suite.results.items[0].iterations == 100); |
| 279 | + try std.testing.expect(suite.results.items[0].avg_ns > 0); |
| 280 | +} |
0 commit comments