Repository navigation
Conversation
… with one allocation Most registered patterns are literal text, :params followed by '/', and an optional trailing *residual. For these there is only one way a path can match, so a left-to-right scan gives exactly the same result and captures as the compiled regexp. Patterns outside that subset keep using the regexp. Serve also attached the router, pattern and method with three context.WithValue calls (five allocations including the boxed strings). A single routedContext answers all three keys from one allocation. Equivalence with the regexp is checked by a table test and a fuzz test.
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.
Router.lookupruns a regexp for every candidate route (in reverse registration order), andRouter.Paramsruns it again withFindStringSubmatch. Since most services register patterns like/things/:idor/things/:id/*rest, this adds up on every request.This PR keeps the regexp as the definition of what a pattern means, but skips it when it is not needed:
newRouteMatcherbuilds a small matcher atRegistertime for patterns made of literal text,:paramsfollowed by/or the end, and an optional trailing*residual. In that subset there is only one way a path can match, so a single left-to-right scan gives the same answer and the same captures as the regexp. Anything else (/:id.json,/*r/:p,*in the middle, invalid UTF-8 or U+FFFD in the pattern) still uses the regexp, so behaviour is unchanged.Serveattached the router, pattern and method with threecontext.WithValuecalls, which is five allocations including the boxed strings. A singleroutedContextnow answers all three keys and delegates everything else (values,Done,Err,Deadline) to the parent, so cancellation still propagates.Testing:
TestRouteMatcherAgreesWithRegexpruns ~1,300 pattern/path pairs (literals, params, residuals, newlines, unicode, invalid UTF-8) and checks the matcher agrees with the compiled regexp on both the match and the captured params.FuzzRouteMatcherAgreesWithRegexpdoes the same with fuzzed inputs; I ran it for 150s (about 11.7M executions) with no differences.TestRouterMixedMatchersPrecedenceinterleaves fast-path and regexp routes to check last-registered-wins still holds, andTestRoutedContextcovers the context values, shadowing, cancellation and the nil-parent panic.go vet ./...,go test ./...andgo test -raceon the router tests pass.Benchmarks (
go test -run '^$' -bench BenchmarkRouter -benchtime 200ms, old and new runs interleaved, benchstat medians, Apple Silicon arm64). I also addedBenchmarkRouterManyRoutes, a 40-route table shaped like a typical service. The timings are from a busy shared machine so they are noisy; the allocation counts are exact.Across all router benchmarks the geomean is -38% time and -46% allocations. The not-found path is unchanged since its cost is building the terror.
I used an AI assistant while writing this; I reviewed the change and ran everything above locally.