a73x

b7e080ca

Make bench + profile workloads representative

a73x   2026-04-17 19:17

Replace the single-burst bench script with a bursty mixed workload
(typing, ls, scrollback, long lines, clears) separated by small sleeps
so the throttled render loop produces distinct frames per burst. Frame
capture goes from 5 to ~65 (debug) / ~180 (ReleaseSafe).

Drop the `sh -c` wrapper in `make profile` — it caused perf
--no-inherit to sample only the shell parent (6 samples), not waystty.
Running perf directly against the binary now captures ~1k samples.

Move workload to tests/bench/workload.sh so it can be tuned without a
zig recompile (embedded via addAnonymousImport). Baseline regenerated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

diff --git a/Makefile b/Makefile
index 087369b..c3fb2cc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
ZIG ?= zig
FLAMEGRAPH ?= flamegraph.pl
STACKCOLLAPSE ?= stackcollapse-perf.pl
FLAMEGRAPH ?= inferno-flamegraph
STACKCOLLAPSE ?= inferno-collapse-perf

.PHONY: build run test bench profile clean test-render golden-update bench-baseline bench-check

@@ -23,8 +23,8 @@ bench: zig-out/bin/waystty

profile:
	$(ZIG) build -Doptimize=ReleaseSafe
	perf record -g -F 999 --no-inherit -o perf.data -- \
		sh -c 'WAYSTTY_BENCH=1 ./zig-out/bin/waystty 2>bench.log'
	WAYSTTY_BENCH=1 perf record -g -F 999 --no-inherit -o perf.data -- \
		./zig-out/bin/waystty 2>bench.log
	perf script -i perf.data \
		| $(STACKCOLLAPSE) \
		| $(FLAMEGRAPH) > flamegraph.svg
diff --git a/build.zig b/build.zig
index 8afb3bf..de3b250 100644
--- a/build.zig
+++ b/build.zig
@@ -86,6 +86,9 @@ pub fn build(b: *std.Build) void {
    exe_mod.addImport("config", config_mod);
    exe_mod.addImport("frame_loop", frame_loop_mod);
    exe_mod.addImport("bench_stats", bench_stats_mod);
    exe_mod.addAnonymousImport("bench_workload", .{
        .root_source_file = b.path("tests/bench/workload.sh"),
    });

    const exe = b.addExecutable(.{
        .name = "waystty",
diff --git a/src/main.zig b/src/main.zig
index 702cfdd..005e784 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -284,7 +284,7 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
    defer alloc.free(shell);

    const bench_script: ?[:0]const u8 = if (is_bench)
        "echo warmup; sleep 0.2; seq 1 50000; find /usr/lib -name '*.so' 2>/dev/null | head -500; yes 'hello world' | head -2000; exit 0"
        @embedFile("bench_workload")
    else
        null;

diff --git a/tests/bench/baseline.json b/tests/bench/baseline.json
index e8b01a6..2f5c98c 100644
--- a/tests/bench/baseline.json
+++ b/tests/bench/baseline.json
@@ -1,38 +1,38 @@
{
  "workload_sha": "066a95eee2d2f6195c0eb997e7e18a63f75b48010b538dd287056f948cc65005",
  "workload_sha": "a79a3b3449f44938e56bc64555a95cc5234c73a1dc4b05c4e8cc17aa0209d1df",
  "zig_version": "0.15.2",
  "waystty_sha": "828c61f589b2bac3785e37531a094d6abb1f40ad",
  "frame_count": 5,
  "waystty_sha": "321b22803777191c86a97ad7ce7edba9b90b6c9e",
  "frame_count": 65,
  "sections": {
    "snapshot": {
      "min": 10,
      "avg": 201,
      "p99": 214,
      "max": 725
      "min": 6,
      "avg": 42,
      "p99": 216,
      "max": 735
    },
    "row_rebuild": {
      "min": 115,
      "avg": 2075,
      "p99": 2166,
      "max": 5447
      "min": 121,
      "avg": 1450,
      "p99": 3344,
      "max": 3383
    },
    "atlas_upload": {
      "min": 0,
      "avg": 10,
      "p99": 0,
      "max": 50
      "avg": 4,
      "p99": 47,
      "max": 57
    },
    "instance_upload": {
      "min": 4,
      "avg": 37,
      "p99": 21,
      "max": 144
      "min": 3,
      "avg": 17,
      "p99": 75,
      "max": 83
    },
    "gpu_submit": {
      "min": 37,
      "avg": 58,
      "p99": 66,
      "max": 79
      "min": 22,
      "avg": 48,
      "p99": 99,
      "max": 106
    }
  }
}
\ No newline at end of file
diff --git a/tests/bench/workload.sh b/tests/bench/workload.sh
new file mode 100644
index 0000000..d58d268
--- /dev/null
+++ b/tests/bench/workload.sh
@@ -0,0 +1,49 @@
#!/bin/sh
# waystty bench workload — bursty mixed traffic so the throttled render
# loop produces distinct frames per burst (instead of one coalesced dump).

printf 'waystty bench workload\n'
sleep 0.1

# Phase 1: typing-like — small lines, small gaps (~60 frames)
i=1
while [ $i -le 60 ]; do
    printf '$ echo hello %d\nhello %d\n' $i $i
    sleep 0.02
    i=$((i + 1))
done

# Phase 2: ls-style bursts (~30 frames)
i=1
while [ $i -le 30 ]; do
    ls /usr/lib 2>/dev/null | head -80
    sleep 0.03
    i=$((i + 1))
done

# Phase 3: scrollback storms (~40 frames)
i=1
while [ $i -le 40 ]; do
    seq 1 500
    sleep 0.025
    i=$((i + 1))
done

# Phase 4: long-line wrapping (~30 frames)
long=$(printf 'x%.0s' $(seq 1 160))
i=1
while [ $i -le 30 ]; do
    printf '%s\n%s\n%s\n' "$long" "$long" "$long"
    sleep 0.03
    i=$((i + 1))
done

# Phase 5: screen-clear + redraw (~20 frames)
i=1
while [ $i -le 20 ]; do
    printf '\033[2J\033[H=== iter %d ===\n' $i
    sleep 0.04
    i=$((i + 1))
done

exit 0