Skip to content

Three gaps that let the perf gate report green through a regression #705

Description

@mparrett

Three items in the performance-gating machinery, grouped because they share a failure mode: the gate reports green while the thing it guards has moved. Each was raised as a non-blocking follow-up during review and never filed.

Related to #663, which asks for a measurement-status discipline. This one is narrower — three specific holes in the instruments #663 would report from.

Checked against main @ 8a8222d0.

1. The 5% budget has never been calibrated against a measured noise floor

bench-ratchet gates at 5%. Nothing establishes what the noise floor is per machine tier, so whether 5% sits above the noise or inside it is unknown. Two datapoints suggest it is closer than it should be.

Byte-identical binaries measured 3.7% and 1.1% apart on two benchmark packages, and a reproducible 4.2% gap came from compile-time code placement alone.

Separately, measuring a candidate one-line change in pkg/vm on an M2 (hyperfine, 3 warmup / 20 runs) gave order-dependent results: main 1.03 ± 0.03x faster with A first, the change 1.07 ± 0.09x faster with B first. The pkg/vm micros ran at ±20–30% σ with no family reaching significance. The change could not be judged at that noise level.

A comment-only edit pushed through the existing dispatch, per tier, would measure the floor directly. Until that number exists, 5% is a guess, and every regression call made against it inherits the guess.

2. jobsSelect truncates to the benchmark family before matching

cmd/bench-ratchet/main.go truncates a benchmark's name to its family — everything before the first / — before applying the -filter regexp. For a slash-specific filter such as -filter '^BenchmarkMapAssoc/HAMT-Assoc/100$', go test does run that exact sub-benchmark, but jobsSelect tests the whole regexp against BenchmarkMapAssoc and returns false.

The consequence inverts the intent. If that targeted sub-benchmark disappears, its baseline row is classified as out of scope and the MISSING signal — the reason to run a filter at all — is suppressed.

3. try-lower-go's fallback stays non-strict

In scripts/ir-stress.lg, try-lower-go's cond ends in :else :ok. Today every status compile-form returns is handled explicitly, so the branch is unreachable and the classification is correct. If compile-form grows a new failure-status map, the census counts it as coverage and the ratchet locks that in.

The sibling path, try-lower-bytecode, was made fail-loud. This one was left as-is deliberately, because changing it needs its own rebaseline. Filing it so the asymmetry is tracked rather than remembered.

Parented under #464.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions