mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
88dd588123
The advisory Benchmarks check failed four pull requests on NormalizeSegment alone in the last 48 paired runs: #2217, #2225, #2252 and #2253. None of them touched the normaliser. On #2252 all five sub-benchmarks rose 25-39% together. Nine other runs moved the rows down by more than 10% instead, -20% to -28% on #2228, #2230 and #2242. Those 13 runs used EPYC 7763, EPYC 9V74 and Intel Xeon runners. The values are quantized. On EPYC 7763 short_name is 2.50ns or 3.12ns and uuid is about 29.6ns or 40.5ns, a cycle or two per call, or one per byte of the UUID scan. Go aligns these functions to 32 bytes, so a change anywhere earlier in the binary can move them between the halves of a 64-byte block. A 32-byte shift is enough: on pulse-dev, adding an unrelated init-time function ahead of the normaliser flipped that parity and failed the old tree the way #2252 did, all five rows +25% to +40% at p=0.000 and nothing else flagged. More samples cannot separate that from a real change, and batching does not help, because every call repeats the same instructions. The route-level rows do not behave this way. Over the same 48 runs no NormalizeRoute row except the root case retired in #2255 moved by more than 10%. with_uuid came closest at 9.2%. The guarantees the benchmark carried now live in deterministic tests. TestNormalizeSegment, TestLooksLikeUUID and TestIsNumeric already pin classification. TestNormalizeSegment_DoesNotAllocate pins zero allocations for the five former inputs, and fails with one allocation per call when the pass-through return copies the segment. It stores each result in a package-level sink: when normalizeSegment was cut down to an inlinable copy, a discarded result let the compiler keep short segments on the stack, and the test missed the allocation. Timing regressions still show in BenchmarkNormalizeRoute, since every path runs its segments through normalizeSegment. The proofs below are paired runs of the CI runner's recipe on pulse-dev, narrowed to ./internal/api/ and raised to 30 samples, because the shared host's load spread these rows by up to 25%, against about 1% on CI runners. - looksLikeUUID scanning twice: check-bench-regression.sh exited 1, flagging only NormalizeRoute/with_uuid, +13.7% (p=0.005). - normalizeSegment doing its work twice: exited 1, flagging only NormalizeRoute/with_uuid, +21.9% (p=0.030). - the candidate against a copy of itself: exited 0. - the candidate against itself plus the same init-time function: exited 0. The route rows still moved 4-5%, and with_uuid 9.2% (p=0.000). The margin for a UUID-scan slowdown is thin. On EPYC 7763 the scan is about 30ns of a 280ns with_uuid call, so doubling it reads as roughly +11% to +14%, while layout alone has moved with_uuid by up to 9.2% both on CI and in the probe above. Giving UUIDs a larger share of a route would make that row flip the way this one did; a smaller share would hide the slowdown.