lib/choir/src/profiling/versus/core/workload.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Kind = enum {
4 saxpy,
5 dot,
6 sum,
7 matmul,
8 polybench_gemm,
9 stencil3,
10 clampsum,
11
12 pub fn symbol(self: Kind) [:0]const u8 {
13 return switch (self) {
14 .saxpy => "kernel_saxpy",
15 .dot => "kernel_dot",
16 .sum => "kernel_sum",
17 .matmul => "kernel_matmul",
18 .polybench_gemm => "kernel_gemm",
19 .stencil3 => "kernel_stencil3",
20 .clampsum => "kernel_clampsum",
21 };
22 }
23
24 pub fn source(self: Kind) []const u8 {
25 return switch (self) {
26 .saxpy => @embedFile("../corpus/saxpy.c"),
27 .dot => @embedFile("../corpus/dot.c"),
28 .sum => @embedFile("../corpus/sum.c"),
29 .matmul => @embedFile("../corpus/matmul.c"),
30 .polybench_gemm => @embedFile("../corpus/polybench_gemm.c"),
31 .stencil3 => @embedFile("../corpus/stencil3.c"),
32 .clampsum => @embedFile("../corpus/clampsum.c"),
33 };
34 }
35 };
36
37 pub const Workload = struct {
38 name: []const u8,
39 kind: Kind,
40 n: u64,
41
42 pub fn elements(self: Workload) u64 {
43 return switch (self.kind) {
44 .matmul, .polybench_gemm => self.n * self.n,
45 else => self.n,
46 };
47 }
48
49 pub fn flops(self: Workload) u64 {
50 return switch (self.kind) {
51 .saxpy => 2 * self.n,
52 .dot => 2 * self.n,
53 .matmul => 2 * self.n * self.n * self.n,
54 .polybench_gemm => 3 * self.n * self.n * self.n + self.n * self.n,
55 .stencil3 => 5 * self.n,
56 .sum, .clampsum => 0,
57 };
58 }
59
60 pub fn movedBytes(self: Workload) u64 {
61 return switch (self.kind) {
62 .saxpy => 12 * self.n,
63 .dot => 16 * self.n,
64 .sum => 8 * self.n,
65 .matmul => 24 * self.n * self.n,
66 .polybench_gemm => 32 * self.n * self.n,
67 .stencil3 => 16 * self.n,
68 .clampsum => 8 * self.n,
69 };
70 }
71 };
72
73 pub const battery = [_]Workload{
74 .{ .name = "saxpy_f32_1m", .kind = .saxpy, .n = 1 << 20 },
75 .{ .name = "dot_f64_1m", .kind = .dot, .n = 1 << 20 },
76 .{ .name = "sum_i64_1m", .kind = .sum, .n = 1 << 20 },
77 .{ .name = "matmul_f64_192", .kind = .matmul, .n = 192 },
78 .{ .name = "polybench_gemm_f64_128", .kind = .polybench_gemm, .n = 128 },
79 .{ .name = "stencil3_f64_1m", .kind = .stencil3, .n = 1 << 20 },
80 .{ .name = "clampsum_i64_1m", .kind = .clampsum, .n = 1 << 20 },
81 };
82
83 pub fn byName(name: []const u8) ?Workload {
84 for (battery) |workload| {
85 if (std.mem.eql(u8, workload.name, name)) return workload;
86 }
87 return null;
88 }
89
90 pub const clamp_lo: i64 = -500;
91 pub const clamp_hi: i64 = 500;
92 pub const gemm_alpha: f64 = 1.5;
93 pub const gemm_beta: f64 = 1.2;
94
95 fn splitmix(index: u64) u64 {
96 var z = index +% 0x9e3779b97f4a7c15;
97 z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9;
98 z = (z ^ (z >> 27)) *% 0x94d049bb133111eb;
99 return z ^ (z >> 31);
100 }
101
102 pub fn fillUnit(comptime T: type, values: []T, offset: u64) void {
103 for (values, 0..) |*value, index| {
104 const bits = splitmix(offset + index);
105 const unit = @as(T, @floatFromInt(bits >> 11)) / @as(T, @floatFromInt(@as(u64, 1) << 53));
106 value.* = unit - 0.5;
107 }
108 }
109
110 pub fn fillInt(values: []i64, offset: u64) void {
111 for (values, 0..) |*value, index| {
112 const bits = splitmix(offset + index);
113 value.* = @as(i64, @intCast(bits % 2001)) - 1000;
114 }
115 }
116
117 test "battery names resolve" {
118 for (battery) |workload| {
119 try std.testing.expectEqual(workload.kind, byName(workload.name).?.kind);
120 }
121 try std.testing.expect(byName("bogus") == null);
122 }
123
124 test "fills are deterministic" {
125 var a: [8]f64 = undefined;
126 var b: [8]f64 = undefined;
127 fillUnit(f64, a[0..], 7);
128 fillUnit(f64, b[0..], 7);
129 for (a, b) |left, right| try std.testing.expectEqual(left, right);
130 for (a) |value| try std.testing.expect(value >= -0.5 and value < 0.5);
131
132 var xs: [8]i64 = undefined;
133 fillInt(xs[0..], 3);
134 for (xs) |value| try std.testing.expect(value >= -1000 and value <= 1000);
135 }