perf(helpers): fast-path type assertion in AsOops before errors.As#134
Merged
Conversation
errors.As is reflection-based and AsOops is called once per layer by
every chain-traversal helper (recursive, getDeepestErrorAttribute,
collectMaps). When the wrapped error is directly an OopsError - the
common case in oops chains - a plain type assertion answers in ~1ns.
errors.As remains as fallback for chains interleaved with non-oops
wrappers (fmt.Errorf %w, custom Unwrap types).
benchstat vs main (darwin/arm64, Apple M3, -count=6 -cpu=1):
│ before.bench.txt │ after.bench.txt │
│ sec/op │ sec/op vs base │
ChainTraversal 1611.5n ± 1% 870.1n ± 0% -46.01% (p=0.002 n=6)
ErrorDeepChain 46.47n ± 5% 46.62n ± 6% ~ (p=0.699 n=6)
ReflectionPaths 514.3n ± 2% 533.9n ± 1% +3.81% (p=0.002 n=6)
geomean 337.7n 278.7n -17.46%
│ before.bench.txt │ after.bench.txt │
│ B/op │ B/op vs base │
ChainTraversal 3.922Ki ± 0% 1.422Ki ± 0% -63.75% (p=0.002 n=6)
│ before.bench.txt │ after.bench.txt │
│ allocs/op │ allocs/op vs base │
ChainTraversal 14.000 ± 0% 6.000 ± 0% -57.14% (p=0.002 n=6)
Note: BenchmarkReflectionPaths shows a stable ~4% time delta with
unchanged allocations even though it does not execute AsOops in its
timed loop - consistent with a binary layout artifact rather than a
logical regression.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #134 +/- ##
==========================================
+ Coverage 90.17% 90.18% +0.01%
==========================================
Files 15 15
Lines 1231 1233 +2
==========================================
+ Hits 1110 1112 +2
Misses 96 96
Partials 25 25
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR optimizes AsOops by adding a fast-path type assertion before falling back to errors.As, reducing chain-traversal overhead when the error is directly an OopsError (the common case for oops-only wrapping chains).
Changes:
- Add a direct
err.(OopsError)type assertion fast path inAsOops. - Retain
errors.Asas a fallback to preserve behavior when chains include non-oops wrappers. - Document the performance rationale inline to clarify why the extra branch exists.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
errors.Asis reflection-based andAsOopsis called once per layer by every chain-traversal helper (recursive,getDeepestErrorAttribute,collectMaps). When the wrapped error is directly anOopsError- the common case in oops chains - a plain type assertion answers in ~1ns.errors.Asremains as a fallback for chains interleaved with non-oops wrappers (fmt.Errorf %w, customUnwraptypes).Benchmarks
benchstatvsmain(darwin/arm64, Apple M3,-count=6 -cpu=1):Note:
BenchmarkReflectionPathsshows a stable ~4% time delta with unchanged allocations even though it does not executeAsOopsin its timed loop - consistent with a binary layout artifact rather than a logical regression.Test plan
Verified with
go build ./...,go test ./..., andgo test -bench=BenchmarkChainTraversal -bench=BenchmarkErrorDeepChain -bench=BenchmarkReflectionPaths -benchmem -count=6 -cpu=1 ./benchmarksrun before/after the change and compared withbenchstat.