Skip to content

fix(bench): interleave baseline and current samples - #415

Merged
jongio merged 1 commit into
mainfrom
fix/412-interleave-bench-samples
Aug 27, 2026
Merged

jongio merged 1 commit into
mainfrom
fix/412-interleave-bench-samples

Conversation

@jongio

@jongio jongio commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Closes #412

Problem

The gate ran every benchmark for the PR code, then every benchmark for the base code. Two contiguous blocks means two separate time windows, so anything that changed the runner between them (cold caches, CPU frequency ramp, a noisy neighbour on a shared VM) shifted one whole block at once. benchstat read that as a large, uniform, statistically significant regression across entire packages, including packages the PR could not reach.

Tight variance and a small p-value did not rule this out. Same-runner comparison controls for runner-to-runner variance, but not for one runner drifting over time.

Three PRs demonstrated it:

PR Files changed linux bench macOS bench
#411 go.mod, go.sum FAIL (filetree) FAIL (crashlog)
#413 one _test.go in internal/github pass FAIL (ai, config)
#414 cmd/theme.go pass FAIL (ai, config)

All three passed every test job on all three platforms, and the same code passed on main after merge. A local benchstat A/B on #411 showed the flagged benchmark 9.77% faster, not slower.

Fix

scripts/bench-interleaved.sh compiles both versions up front, then alternates between them one round at a time. Both versions are sampled across the same window, so drift applies to both equally and cancels in the comparison.

The version that runs first alternates each round, so neither systematically absorbs any per-round warm-up cost.

Total benchmark time is unchanged: 6 rounds of -count=1 costs the same as one -count=6. The extra process launches add roughly a second.

The PR comment now lists which benchmarks were flagged and how to confirm locally, so a future false positive is diagnosable without downloading artifacts (third acceptance criterion in #412).

Verification

Compiled test binaries and benchstat behaviour were checked before writing the script:

  • a go test -c binary emits the full goos/goarch/pkg/cpu header from any working directory
  • benchstat reads concatenated per-round blocks correctly and reports the expected n
  • no benchmark package in BENCH_PKGS depends on testdata or its source directory at runtime

The shell logic was exercised in an isolated scratch repo with a stubbed go, confirming:

  • samples alternate, and the leading version flips each round
  • 6 rounds produce 6 samples per package per side
  • a package present on the PR but absent on base is excluded from both sides rather than skewing the comparison
  • the worktree returns to the commit under test, with no tracked changes left behind

Also bash -n clean, go build ./..., go vet ./..., gofmt -l clean, and go test ./... green.

Note

This PR is its own test. The bench jobs here run through the new interleaved path on both runners.

Acceptance criteria

  • A dependency-only PR with no source changes does not fail the gate on runner drift alone
  • A genuine >15% regression in a source change still fails the gate (threshold and bench-regress.sh logic unchanged)
  • The failure message states which benchmarks regressed on which platform

The gate ran every benchmark for the PR code, then every benchmark for the base
code. Two contiguous blocks means two separate time windows, so anything that
changed the runner between them (cold caches, CPU frequency ramp, a noisy
neighbour) shifted one whole block at once. benchstat read that as a large,
uniform, statistically significant regression across entire packages, including
packages the PR could not reach.

Tight variance and a small p-value did not rule this out. Same-runner
comparison controls for runner-to-runner variance, not for one runner drifting
over time.

Three PRs showed it: #411 (deps only) failed on filetree on linux and crashlog
on macOS; #413 (one _test.go file in internal/github) and #414 (one file in
cmd/) both failed on macOS blaming internal/ai and internal/config. The same
code passed on main after merge. A local benchstat A/B on #411 showed the
flagged benchmark 9.77% faster, not slower.

scripts/bench-interleaved.sh compiles both versions up front, then alternates
between them one round at a time, so both are sampled across the same window
and drift applies equally. The version that runs first alternates each round so
neither absorbs any per-round warm-up cost. Total benchmark time is unchanged.

The PR comment now lists which benchmarks were flagged and how to confirm
locally, so a false positive is diagnosable without downloading artifacts.

Closes #412

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 98ae0c9c-bdbe-418e-8488-15082edca7dc
@github-actions

Copy link
Copy Markdown
Contributor

Benchmarks: darwin-arm64

📊 Results vs main (same-runner) , click to expand

Note

No significant regressions on darwin-arm64 (threshold: 40%, same-runner comparison).

@github-actions

Copy link
Copy Markdown
Contributor

Benchmarks: linux-amd64

📊 Results vs main (same-runner) , click to expand
goos: linux
goarch: amd64
pkg: github.com/jongio/grut/internal/ai
cpu: AMD EPYC 7763 64-Core Processor                
                                       │ bench-baseline.txt │         bench-current.txt          │
                                       │       sec/op       │    sec/op     vs base              │
RedactContent/small_500B-4                      113.7µ ± 4%   113.5µ ±  1%       ~ (p=0.589 n=6)
RedactContent/medium_5KB-4                      1.576m ± 2%   1.573m ±  2%       ~ (p=0.485 n=6)
RedactContent/large_50KB-4                      16.85m ± 2%   16.96m ±  1%       ~ (p=0.394 n=6)
RedactContent_NoMatch/small_500B-4              162.8µ ± 3%   167.3µ ±  4%       ~ (p=0.180 n=6)
RedactContent_NoMatch/medium_5KB-4              1.915m ± 8%   1.900m ± 17%       ~ (p=0.485 n=6)
RedactContent_NoMatch/large_50KB-4              20.22m ± 2%   20.23m ± 26%       ~ (p=0.818 n=6)
ShouldExcludeFile/match_builtin-4               68.32n ± 1%   68.25n ±  1%       ~ (p=0.818 n=6)
ShouldExcludeFile/match_user_pattern-4          2.899µ ± 0%   2.894µ ±  0%       ~ (p=0.195 n=6)
ShouldExcludeFile/no_match-4                    2.010µ ± 0%   2.012µ ±  0%       ~ (p=0.838 n=6)
NewRedactor-4                                   793.7n ± 2%   786.9n ±  1%       ~ (p=0.394 n=6)
geomean                                         75.49µ        75.59µ        +0.14%

                                       │ bench-baseline.txt │         bench-current.txt          │
                                       │        B/op        │     B/op       vs base             │
RedactContent/small_500B-4                 2.877Ki ±   0%     2.879Ki ±  0%  ~ (p=0.675 n=6)
RedactContent/medium_5KB-4                 36.38Ki ±   0%     36.39Ki ±  0%  ~ (p=0.937 n=6)
RedactContent/large_50KB-4                 370.3Ki ±   0%     370.3Ki ±  0%  ~ (p=0.814 n=6)
RedactContent_NoMatch/small_500B-4           4.000 ±    ?       0.000 ±   ?  ~ (p=0.405 n=6)
RedactContent_NoMatch/medium_5KB-4           63.50 ± 188%       45.00 ± 98%  ~ (p=0.710 n=6)
RedactContent_NoMatch/large_50KB-4          126.00 ±  94%       68.00 ± 88%  ~ (p=0.418 n=6)
ShouldExcludeFile/match_builtin-4            0.000 ±   0%       0.000 ±  0%  ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_user_pattern-4       0.000 ±   0%       0.000 ±  0%  ~ (p=1.000 n=6) ¹
ShouldExcludeFile/no_match-4                 0.000 ±   0%       0.000 ±  0%  ~ (p=1.000 n=6) ¹
NewRedactor-4                              1.320Ki ±   0%     1.320Ki ±  0%  ~ (p=1.000 n=6) ¹
geomean                                                   ²                  ?               ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean

                                       │ bench-baseline.txt │         bench-current.txt          │
                                       │     allocs/op      │ allocs/op   vs base                │
RedactContent/small_500B-4                     12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent/medium_5KB-4                     60.00 ± 0%     60.00 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent/large_50KB-4                     483.0 ± 0%     483.0 ± 0%       ~ (p=1.000 n=6)
RedactContent_NoMatch/small_500B-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent_NoMatch/medium_5KB-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent_NoMatch/large_50KB-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_builtin-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_user_pattern-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/no_match-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
NewRedactor-4                                  5.000 ± 0%     5.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                   ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/config
                                   │ bench-baseline.txt │         bench-current.txt         │
                                   │       sec/op       │   sec/op     vs base              │
Validate/valid_defaults-4                   2.404µ ± 1%   2.407µ ± 1%       ~ (p=0.734 n=6)
Validate/with_arrays-4                      9.504µ ± 2%   9.486µ ± 1%       ~ (p=0.937 n=6)
Validate/invalid_multi_error-4              5.451µ ± 1%   5.474µ ± 1%       ~ (p=0.180 n=6)
ParseDefaultsTOML-4                         34.30µ ± 0%   34.26µ ± 0%       ~ (p=0.937 n=6)
RejectEmbeddedKeys/clean_config-4           1.542µ ± 3%   1.557µ ± 2%       ~ (p=0.065 n=6)
RejectEmbeddedKeys/with_patterns-4          9.864µ ± 2%   9.852µ ± 4%       ~ (p=0.699 n=6)
AppendEnumErr/match_first-4                 33.95n ± 1%   34.04n ± 1%       ~ (p=0.197 n=6)
AppendEnumErr/match_last-4                  34.43n ± 1%   34.50n ± 1%       ~ (p=0.623 n=6)
AppendEnumErr/no_match-4                    897.8n ± 1%   900.1n ± 1%       ~ (p=0.310 n=6)
geomean                                     1.599µ        1.602µ       +0.21%

                                   │ bench-baseline.txt │          bench-current.txt           │
                                   │        B/op        │     B/op      vs base                │
Validate/valid_defaults-4                  868.0 ± 0%       868.0 ± 0%       ~ (p=0.636 n=6)
Validate/with_arrays-4                     868.0 ± 0%       868.0 ± 0%       ~ (p=1.000 n=6)
Validate/invalid_multi_error-4           2.038Ki ± 0%     2.039Ki ± 0%       ~ (p=0.729 n=6)
ParseDefaultsTOML-4                      1.836Ki ± 0%     1.836Ki ± 0%       ~ (p=0.545 n=6)
RejectEmbeddedKeys/clean_config-4          0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/with_patterns-4         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_first-4                48.00 ± 0%       48.00 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_last-4                 48.00 ± 0%       48.00 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/no_match-4                   346.0 ± 0%       346.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                               ²                 +0.01%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │     allocs/op      │ allocs/op   vs base                │
Validate/valid_defaults-4                  18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
Validate/with_arrays-4                     18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
Validate/invalid_multi_error-4             58.00 ± 0%     58.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseDefaultsTOML-4                        52.00 ± 0%     52.00 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/clean_config-4          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/with_patterns-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_first-4                1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_last-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/no_match-4                   12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                               ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/crashlog
                                 │ bench-baseline.txt │         bench-current.txt         │
                                 │       sec/op       │   sec/op     vs base              │
ScrubPII/small_100B-4                    12.83µ ±  0%   12.85µ ± 2%       ~ (p=0.180 n=6)
ScrubPII/medium_1KB-4                    226.5µ ±  1%   225.9µ ± 1%       ~ (p=0.394 n=6)
ScrubPII/large_10KB-4                    3.073m ±  1%   3.065m ± 3%       ~ (p=0.310 n=6)
ScrubPII_NoSecrets/100B-4                23.76µ ± 12%   23.62µ ± 1%       ~ (p=0.143 n=6)
ScrubPII_NoSecrets/1KB-4                 234.5µ ±  1%   234.1µ ± 1%       ~ (p=0.937 n=6)
ScrubPII_NoSecrets/10KB-4                2.899m ±  2%   2.963m ± 5%       ~ (p=0.240 n=6)
ScrubLogTail/10_entries-4                129.3µ ±  1%   129.3µ ± 1%       ~ (p=0.485 n=6)
ScrubLogTail/50_entries-4                648.1µ ±  2%   647.7µ ± 1%       ~ (p=0.818 n=6)
ScrubLogTail/100_entries_mixed-4         10.81m ±  2%   10.84m ± 1%       ~ (p=0.699 n=6)
geomean                                  370.2µ         370.7µ       +0.14%

                                 │ bench-baseline.txt │         bench-current.txt          │
                                 │        B/op        │     B/op      vs base              │
ScrubPII/small_100B-4                      913.5 ± 0%     914.0 ± 0%       ~ (p=0.457 n=6)
ScrubPII/medium_1KB-4                    11.89Ki ± 0%   11.90Ki ± 0%       ~ (p=0.554 n=6)
ScrubPII/large_10KB-4                    228.6Ki ± 0%   228.4Ki ± 0%       ~ (p=0.132 n=6)
ScrubPII_NoSecrets/100B-4                1.173Ki ± 0%   1.173Ki ± 0%       ~ (p=0.905 n=6)
ScrubPII_NoSecrets/1KB-4                 10.09Ki ± 0%   10.10Ki ± 0%       ~ (p=0.284 n=6)
ScrubPII_NoSecrets/10KB-4                101.4Ki ± 1%   101.3Ki ± 1%       ~ (p=0.310 n=6)
ScrubLogTail/10_entries-4                9.081Ki ± 0%   9.091Ki ± 0%       ~ (p=0.100 n=6)
ScrubLogTail/50_entries-4                45.52Ki ± 0%   45.50Ki ± 0%       ~ (p=0.851 n=6)
ScrubLogTail/100_entries_mixed-4         545.9Ki ± 0%   546.1Ki ± 0%       ~ (p=0.394 n=6)
geomean                                  20.56Ki        20.56Ki       +0.01%

                                 │ bench-baseline.txt │          bench-current.txt          │
                                 │     allocs/op      │  allocs/op   vs base                │
ScrubPII/small_100B-4                      17.00 ± 0%    17.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII/medium_1KB-4                      27.00 ± 0%    27.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII/large_10KB-4                      80.00 ± 1%    79.50 ± 1%       ~ (p=1.000 n=6)
ScrubPII_NoSecrets/100B-4                  15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII_NoSecrets/1KB-4                   15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII_NoSecrets/10KB-4                  15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/10_entries-4                  171.0 ± 0%    171.0 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/50_entries-4                  851.0 ± 0%    851.0 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/100_entries_mixed-4          1.909k ± 0%   1.909k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                    68.78         68.73       -0.07%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/git
                               │ bench-baseline.txt │         bench-current.txt          │
                               │       sec/op       │    sec/op     vs base              │
ParseStatus/10_files-4                  1.815µ ± 2%   1.800µ ±  1%  -0.80% (p=0.026 n=6)
ParseStatus/100_files-4                 15.94µ ± 1%   15.78µ ±  4%       ~ (p=0.065 n=6)
ParseStatus/500_files-4                 76.96µ ± 1%   75.73µ ±  1%  -1.60% (p=0.009 n=6)
ParseStatus/100_renamed-4               23.83µ ± 2%   23.53µ ±  2%       ~ (p=0.132 n=6)
ParseStatusBranch-4                     1.710µ ± 4%   1.703µ ±  1%       ~ (p=0.141 n=6)
ParseDiff/10_lines-4                    1.082µ ± 4%   1.072µ ±  1%       ~ (p=0.058 n=6)
ParseDiff/100_lines-4                   6.764µ ± 2%   6.747µ ±  4%       ~ (p=0.818 n=6)
ParseDiff/1000_lines-4                  68.41µ ± 2%   67.68µ ±  2%       ~ (p=0.818 n=6)
ParseHunkHeader/simple-4                100.4n ± 1%   100.2n ±  0%       ~ (p=0.219 n=6)
ParseHunkHeader/with_context-4          106.3n ± 1%   106.4n ±  2%       ~ (p=0.981 n=6)
ParseHunkHeader/large_range-4           118.8n ± 1%   119.1n ±  1%       ~ (p=0.970 n=6)
UndoStack/record_50-4                   20.85µ ± 1%   20.68µ ±  2%       ~ (p=0.394 n=6)
UndoStack/record_overflow-4             81.68µ ± 1%   81.37µ ± 16%       ~ (p=0.699 n=6)
UndoStack/peek_undo-4                   8.473n ± 0%   8.476n ±  0%       ~ (p=0.970 n=6)
UndoStack/can_undo_redo-4               11.39n ± 0%   11.36n ±  1%  -0.31% (p=0.050 n=6)
JoinPaths-4                             1.228µ ± 2%   1.216µ ±  2%       ~ (p=0.290 n=6)
SplitPaths-4                            2.259µ ± 3%   2.238µ ±  4%       ~ (p=0.310 n=6)
geomean                                 1.819µ        1.808µ        -0.63%

                               │ bench-baseline.txt │          bench-current.txt           │
                               │        B/op        │     B/op      vs base                │
ParseStatus/10_files-4               2.641Ki ± 0%     2.641Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_files-4              24.66Ki ± 0%     24.66Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/500_files-4              111.9Ki ± 0%     111.9Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_renamed-4            32.16Ki ± 0%     32.16Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatusBranch-4                    928.0 ± 0%       928.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/10_lines-4                 1.703Ki ± 0%     1.703Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/100_lines-4                11.02Ki ± 0%     11.02Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/1000_lines-4               116.5Ki ± 0%     116.5Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/simple-4               32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/with_context-4         32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/large_range-4          32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_50-4                22.91Ki ± 0%     22.91Ki ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_overflow-4          87.27Ki ± 0%     87.27Ki ± 0%       ~ (p=0.567 n=6)
UndoStack/peek_undo-4                  0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/can_undo_redo-4              0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
JoinPaths-4                          2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
SplitPaths-4                         6.062Ki ± 0%     6.062Ki ± 0%       ~ (p=1.000 n=6) ¹
geomean                                           ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                               │ bench-baseline.txt │         bench-current.txt          │
                               │     allocs/op      │ allocs/op   vs base                │
ParseStatus/10_files-4                 14.00 ± 0%     14.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_files-4                89.00 ± 0%     89.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/500_files-4                411.0 ± 0%     411.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_renamed-4              209.0 ± 0%     209.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatusBranch-4                    2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/10_lines-4                   9.000 ± 0%     9.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/100_lines-4                  46.00 ± 0%     46.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/1000_lines-4                 448.0 ± 0%     448.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/simple-4               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/with_context-4         1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/large_range-4          1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_50-4                  208.0 ± 0%     208.0 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_overflow-4            811.0 ± 0%     811.0 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/peek_undo-4                  0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/can_undo_redo-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
JoinPaths-4                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
SplitPaths-4                           7.000 ± 0%     7.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                           ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/markdown
                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │       sec/op       │    sec/op     vs base              │
RenderStaticCached/same_width-4             544.1µ ± 3%   545.9µ ±  1%       ~ (p=0.485 n=6)
RenderStaticCached/varying_width-4          620.9µ ± 3%   620.8µ ±  1%       ~ (p=0.699 n=6)
RenderStaticCold-4                          567.4µ ± 9%   563.7µ ± 12%       ~ (p=0.240 n=6)
geomean                                     576.6µ        575.9µ        -0.11%

                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │        B/op        │     B/op      vs base              │
RenderStaticCached/same_width-4            122.1Ki ± 0%   122.0Ki ± 0%       ~ (p=0.394 n=6)
RenderStaticCached/varying_width-4         144.1Ki ± 0%   144.3Ki ± 0%       ~ (p=0.394 n=6)
RenderStaticCold-4                         161.7Ki ± 1%   161.4Ki ± 1%       ~ (p=0.589 n=6)
geomean                                    141.7Ki        141.6Ki       -0.04%

                                   │ bench-baseline.txt │          bench-current.txt          │
                                   │     allocs/op      │  allocs/op   vs base                │
RenderStaticCached/same_width-4             12.18k ± 0%   12.18k ± 0%       ~ (p=1.000 n=6) ¹
RenderStaticCached/varying_width-4          13.77k ± 0%   13.76k ± 0%       ~ (p=0.719 n=6)
RenderStaticCold-4                          5.824k ± 0%   5.824k ± 0%       ~ (p=0.446 n=6)
geomean                                     9.919k        9.919k       -0.01%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/notify
                           │ bench-baseline.txt │         bench-current.txt         │
                           │       sec/op       │   sec/op     vs base              │
NotifyToastChurn-4                  330.7n ± 6%   331.4n ± 1%       ~ (p=0.615 n=6)
NotifyToastLifecycleCold-4          668.9n ± 3%   672.5n ± 2%       ~ (p=0.937 n=6)
NotifyInlineCap-4                   1.024µ ± 2%   1.025µ ± 2%       ~ (p=0.903 n=6)
geomean                             609.6n        611.2n       +0.27%

                           │ bench-baseline.txt │         bench-current.txt          │
                           │        B/op        │    B/op     vs base                │
NotifyToastChurn-4                   144.0 ± 0%   144.0 ± 0%       ~ (p=1.000 n=6) ¹
NotifyToastLifecycleCold-4           848.0 ± 0%   848.0 ± 0%       ~ (p=1.000 n=6) ¹
NotifyInlineCap-4                    104.0 ± 0%   104.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                              233.3        233.3       +0.00%
¹ all samples are equal

                           │ bench-baseline.txt │         bench-current.txt          │
                           │     allocs/op      │ allocs/op   vs base                │
NotifyToastChurn-4                   2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
NotifyToastLifecycleCold-4           10.00 ± 0%   10.00 ± 0%       ~ (p=1.000 n=6) ¹
NotifyInlineCap-4                    2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                              3.420        3.420       +0.00%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/panels/filetree
                                               │ bench-baseline.txt │         bench-current.txt         │
                                               │       sec/op       │   sec/op     vs base              │
TreeRebuildVisible/flat_50_files-4                      260.2n ± 0%   260.4n ± 0%       ~ (p=0.582 n=6)
TreeRebuildVisible/nested_5_levels_461_nodes-4          2.861µ ± 1%   2.855µ ± 0%       ~ (p=0.723 n=6)
TreeRebuildVisible/large_1000_nodes-4                   5.356µ ± 1%   5.358µ ± 0%       ~ (p=0.853 n=6)
RenderLine/file_node-4                                  4.606µ ± 0%   4.604µ ± 0%       ~ (p=0.974 n=6)
RenderLine/dir_node_cursor-4                            6.056µ ± 1%   6.094µ ± 3%  +0.64% (p=0.041 n=6)
RenderLine/with_icons-4                                 4.736µ ± 0%   4.715µ ± 2%       ~ (p=0.121 n=6)
DetachedLazyLoad/50_entries-4                           150.7µ ± 1%   150.3µ ± 2%       ~ (p=0.240 n=6)
DetachedLazyLoad/23_root_entries-4                      67.22µ ± 1%   67.39µ ± 1%       ~ (p=0.589 n=6)
DisplayWidth/ascii-4                                    67.74n ± 1%   67.98n ± 1%  +0.35% (p=0.045 n=6)
DisplayWidth/mixed_unicode-4                            310.5n ± 1%   312.0n ± 1%       ~ (p=0.589 n=6)
TruncateToWidth/no_truncation-4                         21.77n ± 1%   21.57n ± 1%       ~ (p=0.331 n=6)
TruncateToWidth/needs_truncation-4                      852.8n ± 1%   853.4n ± 0%       ~ (p=0.621 n=6)
geomean                                                 1.890µ        1.890µ       -0.00%

                                               │ bench-baseline.txt │          bench-current.txt           │
                                               │        B/op        │     B/op      vs base                │
TreeRebuildVisible/flat_50_files-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/nested_5_levels_461_nodes-4         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/large_1000_nodes-4                  0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/file_node-4                                 392.0 ± 0%       392.0 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/dir_node_cursor-4                           624.0 ± 0%       624.0 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/with_icons-4                                392.0 ± 0%       392.0 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/50_entries-4                        34.12Ki ± 0%     34.13Ki ± 0%       ~ (p=0.669 n=6)
DetachedLazyLoad/23_root_entries-4                   14.77Ki ± 0%     14.77Ki ± 0%       ~ (p=0.420 n=6)
DisplayWidth/ascii-4                                   0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/mixed_unicode-4                           0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/no_truncation-4                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/needs_truncation-4                     544.0 ± 0%       544.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                           ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                               │ bench-baseline.txt │         bench-current.txt          │
                                               │     allocs/op      │ allocs/op   vs base                │
TreeRebuildVisible/flat_50_files-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/nested_5_levels_461_nodes-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/large_1000_nodes-4                  0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/file_node-4                                 12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/dir_node_cursor-4                           18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/with_icons-4                                12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/50_entries-4                          363.0 ± 0%     363.0 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/23_root_entries-4                     164.0 ± 0%     164.0 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/ascii-4                                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/mixed_unicode-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/no_truncation-4                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/needs_truncation-4                     3.000 ± 0%     3.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                           ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/panels/gitdiff
                                                   │ bench-baseline.txt │         bench-current.txt         │
                                                   │       sec/op       │   sec/op     vs base              │
InlineDiffRenderExact/10_lines-4                            47.48µ ± 1%   47.44µ ± 0%       ~ (p=0.974 n=6)
InlineDiffRenderExact/100_lines-4                           412.5µ ± 1%   412.8µ ± 0%       ~ (p=0.699 n=6)
InlineDiffRenderExact/1000_lines-4                          4.133m ± 0%   4.141m ± 1%       ~ (p=0.240 n=6)
InlineDiffRenderCold-4                                      412.6µ ± 1%   413.9µ ± 1%       ~ (p=0.310 n=6)
SideBySideDiffRenderExact/10_lines-4                        86.77µ ± 1%   86.51µ ± 1%       ~ (p=0.310 n=6)
SideBySideDiffRenderExact/100_lines-4                       778.3µ ± 2%   774.4µ ± 1%       ~ (p=0.310 n=6)
SideBySideDiffRenderExact/1000_lines-4                      7.841m ± 1%   7.832m ± 1%       ~ (p=0.394 n=6)
SideBySideDiffRenderCold-4                                  779.7µ ± 1%   776.6µ ± 1%       ~ (p=0.310 n=6)
PairDiffLines/20_lines_mixed-4                              307.0n ± 2%   307.7n ± 1%       ~ (p=0.699 n=6)
PairDiffLines/200_lines_mixed-4                             2.072µ ± 7%   2.092µ ± 2%       ~ (p=0.132 n=6)
RenderViewportWithStats/small-4                             5.606µ ± 2%   5.636µ ± 1%       ~ (p=0.485 n=6)
RenderViewportWithStats/large_scrolled-4                    5.961µ ± 2%   5.936µ ± 1%       ~ (p=0.699 n=6)
DiffWordsBoundedAllocations/50_tokens-4                     30.72µ ± 1%   30.70µ ± 0%       ~ (p=0.310 n=6)
DiffWordsBoundedAllocations/200_tokens-4                    453.1µ ± 0%   452.0µ ± 0%       ~ (p=0.180 n=6)
DiffWordsBoundedAllocations/400_tokens-4                    1.780m ± 0%   1.779m ± 0%       ~ (p=0.394 n=6)
DiffWordsBoundedAllocations/over_cap_tokens-4               5.167µ ± 3%   5.099µ ± 1%  -1.31% (p=0.026 n=6)
DiffWordsBoundedAllocations/one_mib_over_cap-4              5.135µ ± 4%   5.175µ ± 2%       ~ (p=0.937 n=6)
DiffWordsBoundedAllocations/one_mib_single_token-4          9.828m ± 0%   9.841m ± 0%       ~ (p=0.132 n=6)
geomean                                                     103.6µ        103.6µ       -0.01%

                                                   │ bench-baseline.txt │          bench-current.txt           │
                                                   │        B/op        │     B/op      vs base                │
InlineDiffRenderExact/10_lines-4                           4.016Ki ± 0%   4.016Ki ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/100_lines-4                          33.47Ki ± 0%   33.47Ki ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/1000_lines-4                         334.7Ki ± 0%   334.7Ki ± 0%       ~ (p=1.000 n=6)
InlineDiffRenderCold-4                                     37.97Ki ± 0%   37.97Ki ± 0%       ~ (p=1.000 n=6)
SideBySideDiffRenderExact/10_lines-4                       15.50Ki ± 0%   15.50Ki ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/100_lines-4                      144.3Ki ± 0%   144.3Ki ± 0%       ~ (p=0.708 n=6)
SideBySideDiffRenderExact/1000_lines-4                     1.410Mi ± 0%   1.410Mi ± 0%       ~ (p=0.165 n=6)
SideBySideDiffRenderCold-4                                 148.8Ki ± 0%   148.8Ki ± 0%       ~ (p=1.000 n=6)
PairDiffLines/20_lines_mixed-4                               416.0 ± 0%     416.0 ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/200_lines_mixed-4                            3.469Ki ± 0%   3.469Ki ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/small-4                            6.368Ki ± 0%   6.368Ki ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/large_scrolled-4                   6.376Ki ± 0%   6.376Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/50_tokens-4                    4.125Ki ± 0%   4.125Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/200_tokens-4                   18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/400_tokens-4                   36.62Ki ± 0%   36.62Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/over_cap_tokens-4              18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_over_cap-4             18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_single_token-4           896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                    17.94Ki        17.94Ki       -0.00%
¹ all samples are equal

                                                   │ bench-baseline.txt │          bench-current.txt          │
                                                   │     allocs/op      │  allocs/op   vs base                │
InlineDiffRenderExact/10_lines-4                             122.0 ± 0%    122.0 ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/100_lines-4                           1.096k ± 0%   1.096k ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/1000_lines-4                          10.96k ± 0%   10.96k ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderCold-4                                      1.110k ± 0%   1.110k ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/10_lines-4                         217.0 ± 0%    217.0 ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/100_lines-4                       2.044k ± 0%   2.044k ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/1000_lines-4                      20.44k ± 0%   20.44k ± 0%       ~ (p=1.000 n=6)
SideBySideDiffRenderCold-4                                  2.058k ± 0%   2.058k ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/20_lines_mixed-4                               3.000 ± 0%    3.000 ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/200_lines_mixed-4                              3.000 ± 0%    3.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/small-4                              18.00 ± 0%    18.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/large_scrolled-4                     19.00 ± 0%    19.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/50_tokens-4                      8.000 ± 0%    8.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/200_tokens-4                     12.00 ± 0%    12.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/400_tokens-4                     14.00 ± 0%    14.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/over_cap_tokens-4                6.000 ± 0%    6.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_over_cap-4               6.000 ± 0%    6.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_single_token-4           4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                      80.02         80.02       +0.00%
¹ all samples are equal

Note

No significant regressions on linux-amd64 (threshold: 15%, same-runner comparison).

@jongio
jongio merged commit 8f383e2 into main Aug 27, 2026
9 checks passed
@jongio
jongio deleted the fix/412-interleave-bench-samples branch August 27, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark regression gate produces false positives on dependency PRs

1 participant