summaryrefslogtreecommitdiff
path: root/test/perf.zig
diff options
context:
space:
mode:
Diffstat (limited to 'test/perf.zig')
-rw-r--r--test/perf.zig94
1 files changed, 59 insertions, 35 deletions
diff --git a/test/perf.zig b/test/perf.zig
index 368e6ce1..bf5f0200 100644
--- a/test/perf.zig
+++ b/test/perf.zig
@@ -166,8 +166,8 @@ pub fn main(init: std.process.Init) !void {
/// big a difference has to be before it is real.
fn measure(op: Op, fx: Fixture, path: []const u8, reps: usize) !Cell {
const warmup = 3;
- var samples: std.ArrayList(u64) = .empty;
- defer samples.deinit(gpa);
+ const samples = try gpa.alloc(u64, reps);
+ defer gpa.free(samples);
if (op == .open) {
// fresh core per sample: opening is a one-shot, and the second Look at
@@ -180,9 +180,9 @@ fn measure(op: Op, fx: Fixture, path: []const u8, reps: usize) !Cell {
pump(core);
_ = try frame(core);
const dt = nowNs() -| t0;
- if (n >= warmup) try samples.append(gpa, dt / 1000);
+ if (n >= warmup) samples[n - warmup] = dt / 1000;
}
- return summarize(samples.items);
+ return summarize(samples);
}
const core = try boot();
@@ -217,9 +217,9 @@ fn measure(op: Op, fx: Fixture, path: []const u8, reps: usize) !Cell {
pump(core);
_ = try frame(core);
const dt = nowNs() -| t0;
- if (n >= warmup) try samples.append(gpa, dt / 1000);
+ if (n >= warmup) samples[n - warmup] = dt / 1000;
}
- return summarize(samples.items);
+ return summarize(samples);
}
/// Park the cursor and the view at sample `n`'s position, walked across the
@@ -291,22 +291,39 @@ fn summarize(samples: []u64) Cell {
/// lines get their width from a comment tail — the alternative (an enormous
/// string literal) makes the whole file one token and flatters every scan that
/// looks for a newline.
+fn generatedPrefixLen(n: usize) usize {
+ return switch (n % 4) {
+ 0 => std.fmt.count("const value_{d}: u32 = {d}; // ", .{ n, n *% 2654435761 }),
+ 1 => std.fmt.count("pub fn helper_{d}(a: u32, b: u32) u32 {{ return a +% b *% {d}; }} // ", .{ n, n }),
+ 2 => std.fmt.count(" const text_{d} = \"lorem ipsum dolor sit amet {d}\"; // ", .{ n, n }),
+ else => std.fmt.count("// comment line {d} — ", .{n}),
+ };
+}
+
+fn writeGeneratedPrefix(out: *std.Io.Writer, n: usize) !void {
+ switch (n % 4) {
+ 0 => try out.print("const value_{d}: u32 = {d}; // ", .{ n, n *% 2654435761 }),
+ 1 => try out.print("pub fn helper_{d}(a: u32, b: u32) u32 {{ return a +% b *% {d}; }} // ", .{ n, n }),
+ 2 => try out.print(" const text_{d} = \"lorem ipsum dolor sit amet {d}\"; // ", .{ n, n }),
+ else => try out.print("// comment line {d} — ", .{n}),
+ }
+}
+
fn generate(fx: Fixture) ![]u8 {
- var out: std.ArrayList(u8) = .empty;
- try out.ensureTotalCapacity(gpa, fx.lines * (fx.cols + 1) + 64);
- var n: usize = 0;
- while (n < fx.lines) : (n += 1) {
- const start = out.items.len;
- switch (n % 4) {
- 0 => try out.print(gpa, "const value_{d}: u32 = {d}; // ", .{ n, n *% 2654435761 }),
- 1 => try out.print(gpa, "pub fn helper_{d}(a: u32, b: u32) u32 {{ return a +% b *% {d}; }} // ", .{ n, n }),
- 2 => try out.print(gpa, " const text_{d} = \"lorem ipsum dolor sit amet {d}\"; // ", .{ n, n }),
- else => try out.print(gpa, "// comment line {d} — ", .{n}),
- }
- while (out.items.len - start < fx.cols) try out.append(gpa, 'x');
- try out.append(gpa, '\n');
+ var byte_len: usize = 0;
+ for (0..fx.lines) |n| byte_len += @max(generatedPrefixLen(n), fx.cols) + 1;
+ const text = try gpa.alloc(u8, byte_len);
+ errdefer gpa.free(text);
+ var out: std.Io.Writer = .fixed(text);
+ for (0..fx.lines) |n| {
+ const start = out.end;
+ try writeGeneratedPrefix(&out, n);
+ const prefix_len = out.end - start;
+ if (prefix_len < fx.cols) try out.splatByteAll('x', fx.cols - prefix_len);
+ try out.writeByte('\n');
}
- return out.toOwnedSlice(gpa);
+ std.debug.assert(out.end == text.len);
+ return text;
}
fn writeFile(path: []const u8, text: []const u8) !void {
@@ -387,28 +404,30 @@ fn reportText(cells: *const [std.enums.values(Op).len][fixtures.len]Cell, bytes:
/// Real stdout, not std.debug.print's stderr: this is the form `--base` reads
/// back, and `zig build perf -- --json > runs/old.json` writing an empty file
/// would make the next comparison silently print no ratios at all.
+const json_report_max_bytes = 32 * 1024;
+
fn reportJson(io: std.Io, cells: *const [std.enums.values(Op).len][fixtures.len]Cell, bytes: *const [fixtures.len]usize, reps: usize) void {
- var out: std.ArrayList(u8) = .empty;
- defer out.deinit(gpa);
- out.print(gpa, "{{\"cols\":{d},\"rows\":{d},\"reps\":{d},\"fixtures\":[", .{ screen_cols, screen_rows, reps }) catch return;
+ var storage: [json_report_max_bytes]u8 = undefined;
+ var out: std.Io.Writer = .fixed(&storage);
+ out.print("{{\"cols\":{d},\"rows\":{d},\"reps\":{d},\"fixtures\":[", .{ screen_cols, screen_rows, reps }) catch return;
for (fixtures, 0..) |fx, fi| {
- out.print(gpa, "{s}{{\"name\":\"{s}\",\"lines\":{d},\"cols\":{d},\"bytes\":{d}}}", .{
+ out.print("{s}{{\"name\":\"{s}\",\"lines\":{d},\"cols\":{d},\"bytes\":{d}}}", .{
if (fi > 0) "," else "", fx.name, fx.lines, fx.cols, bytes[fi],
}) catch return;
}
- out.appendSlice(gpa, "],\"cells\":[") catch return;
+ out.writeAll("],\"cells\":[") catch return;
var first = true;
for (std.enums.values(Op), 0..) |op, oi| {
for (fixtures, 0..) |fx, fi| {
const c = cells[oi][fi];
- out.print(gpa, "{s}{{\"op\":\"{s}\",\"fixture\":\"{s}\",\"min_us\":{d},\"med_us\":{d},\"p90_us\":{d},\"max_us\":{d}}}", .{
+ out.print("{s}{{\"op\":\"{s}\",\"fixture\":\"{s}\",\"min_us\":{d},\"med_us\":{d},\"p90_us\":{d},\"max_us\":{d}}}", .{
if (first) "" else ",", op.label(), fx.name, c.min_us, c.med_us, c.p90_us, c.max_us,
}) catch return;
first = false;
}
}
- out.appendSlice(gpa, "]}\n") catch return;
- std.Io.File.stdout().writeStreamingAll(io, out.items) catch {};
+ out.writeAll("]}\n") catch return;
+ std.Io.File.stdout().writeStreamingAll(io, out.buffered()) catch {};
}
const Base = struct { med: [std.enums.values(Op).len][fixtures.len]u64 };
@@ -420,6 +439,7 @@ const Base = struct { med: [std.enums.values(Op).len][fixtures.len]u64 };
/// and did not get is worse than no comparison.
fn readBase(path: []const u8) ?Base {
const src = readFileAlloc(path) catch fatal("--base: cannot read {s}", .{path});
+ defer gpa.free(src);
var b: Base = .{ .med = @splat(@splat(0)) };
const parsed = std.json.parseFromSlice(struct {
cells: []const struct {
@@ -446,18 +466,22 @@ fn readFileAlloc(path: []const u8) ![]u8 {
const fd = libc.open(path_z, .{ .ACCMODE = .RDONLY });
if (fd < 0) return error.OpenFailed;
defer _ = libc.close(fd);
- var buf: std.ArrayList(u8) = .empty;
- var chunk: [16384]u8 = undefined;
- while (true) {
- const n = libc.read(fd, &chunk, chunk.len);
+ const end = libc.lseek(fd, 0, libc.SEEK.END);
+ if (end < 0 or libc.lseek(fd, 0, libc.SEEK.SET) < 0) return error.StatFailed;
+ const size = std.math.cast(usize, end) orelse return error.FileTooLarge;
+ const buf = try gpa.alloc(u8, size);
+ errdefer gpa.free(buf);
+ var offset: usize = 0;
+ while (offset < buf.len) {
+ const n = libc.read(fd, buf.ptr + offset, buf.len - offset);
if (n < 0) {
if (libc.errno(n) == .INTR) continue;
return error.ReadFailed;
}
- if (n == 0) break;
- try buf.appendSlice(gpa, chunk[0..@intCast(n)]);
+ if (n == 0) return error.UnexpectedEndOfFile;
+ offset += @intCast(n);
}
- return buf.items;
+ return buf;
}
fn fatal(comptime fmt: []const u8, args: anytype) noreturn {