From 1b26cf6b5eefb75899cbe4b97fd0c048eded6591 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Mon, 10 Mar 2025 16:04:04 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20optimize=20rou?= =?UTF-8?q?teParser=20by=20using=20sync.Pool=20(#3343)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ Refactor: add routerParser pool ``` goos: linux goarch: amd64 pkg: github.com/gofiber/fiber/v3 cpu: AMD EPYC 9J14 96-Core Processor │ ori.txt │ pool.txt │ │ sec/op │ sec/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16 173.9n ± 0% 159.3n ± 1% -8.37% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16 163.9n ± 0% 150.9n ± 0% -7.90% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16 165.4n ± 1% 150.6n ± 1% -8.95% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16 174.9n ± 0% 160.6n ± 0% -8.15% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16 520.2n ± 0% 438.1n ± 1% -15.78% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16 521.8n ± 0% 436.8n ± 0% -16.29% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16 630.0n ± 0% 525.0n ± 0% -16.67% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16 633.3n ± 0% 526.4n ± 0% -16.89% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16 627.8n ± 0% 527.5n ± 0% -15.97% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16 602.1n ± 0% 501.9n ± 0% -16.65% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16 604.9n ± 0% 504.3n ± 0% -16.62% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16 616.7n ± 0% 512.8n ± 1% -16.86% (p=0.000 n=20) geomean 390.5n 336.5n -13.84% │ ori.txt │ pool.txt │ │ B/op │ B/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16 152.0 ± 0% 144.0 ± 0% -5.26% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16 144.0 ± 0% 136.0 ± 0% -5.56% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16 144.0 ± 0% 136.0 ± 0% -5.56% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16 160.0 ± 0% 152.0 ± 0% -5.00% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16 440.0 ± 0% 368.0 ± 0% -16.36% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16 440.0 ± 0% 368.0 ± 0% -16.36% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16 536.0 ± 0% 432.0 ± 0% -19.40% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16 536.0 ± 0% 432.0 ± 0% -19.40% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16 536.0 ± 0% 432.0 ± 0% -19.40% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16 528.0 ± 0% 424.0 ± 0% -19.70% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16 528.0 ± 0% 424.0 ± 0% -19.70% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16 528.0 ± 0% 424.0 ± 0% -19.70% (p=0.000 n=20) geomean 337.9 288.8 -14.52% │ ori.txt │ pool.txt │ │ allocs/op │ allocs/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16 5.000 ± 0% 4.000 ± 0% -20.00% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16 5.000 ± 0% 4.000 ± 0% -20.00% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16 5.000 ± 0% 4.000 ± 0% -20.00% (p=0.000 n=20) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16 5.000 ± 0% 4.000 ± 0% -20.00% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16 13.000 ± 0% 9.000 ± 0% -30.77% (p=0.000 n=20) _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16 13.000 ± 0% 9.000 ± 0% -30.77% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16 14.000 ± 0% 9.000 ± 0% -35.71% (p=0.000 n=20) geomean 9.811 6.868 -29.99% ``` * 🩹 Fix: golangci-lint problem --- path.go | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/path.go b/path.go index 76236e40..3876943d 100644 --- a/path.go +++ b/path.go @@ -11,6 +11,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" "unicode" @@ -26,6 +27,12 @@ type routeParser struct { plusCount int // number of plus parameters, used internally to give the plus parameter its number } +var routerParserPool = &sync.Pool{ + New: func() any { + return &routeParser{} + }, +} + // routeSegment holds the segment metadata type routeSegment struct { // const information @@ -163,7 +170,10 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool { patternPretty = utils.TrimRight(patternPretty, '/') } - parser := parseRoute(string(patternPretty)) + parser, _ := routerParserPool.Get().(*routeParser) //nolint:errcheck // only contains routeParser + parser.reset() + parser.parseRoute(string(patternPretty)) + defer routerParserPool.Put(parser) if string(patternPretty) == "/" && path == "/" { return true @@ -184,10 +194,16 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool { return string(patternPretty) == path } +func (parser *routeParser) reset() { + parser.segs = parser.segs[:0] + parser.params = parser.params[:0] + parser.wildCardCount = 0 + parser.plusCount = 0 +} + // parseRoute analyzes the route and divides it into segments for constant areas and parameters, // this information is needed later when assigning the requests to the declared routes -func parseRoute(pattern string, customConstraints ...CustomConstraint) routeParser { - parser := routeParser{} +func (parser *routeParser) parseRoute(pattern string, customConstraints ...CustomConstraint) { var n int var seg *routeSegment for len(pattern) > 0 { @@ -207,7 +223,13 @@ func parseRoute(pattern string, customConstraints ...CustomConstraint) routePars parser.segs[len(parser.segs)-1].IsLast = true } parser.segs = addParameterMetaInfo(parser.segs) +} +// parseRoute analyzes the route and divides it into segments for constant areas and parameters, +// this information is needed later when assigning the requests to the declared routes +func parseRoute(pattern string, customConstraints ...CustomConstraint) routeParser { + parser := routeParser{} + parser.parseRoute(pattern, customConstraints...) return parser } @@ -290,7 +312,7 @@ func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) ( } // analyseParameterPart find the parameter end and create the route segment -func (routeParser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (int, *routeSegment) { +func (parser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (int, *routeSegment) { isWildCard := pattern[0] == wildcardParam isPlusParam := pattern[0] == plusParam @@ -377,11 +399,11 @@ func (routeParser *routeParser) analyseParameterPart(pattern string, customConst // add access iterator to wildcard and plus if isWildCard { - routeParser.wildCardCount++ - paramName += strconv.Itoa(routeParser.wildCardCount) + parser.wildCardCount++ + paramName += strconv.Itoa(parser.wildCardCount) } else if isPlusParam { - routeParser.plusCount++ - paramName += strconv.Itoa(routeParser.plusCount) + parser.plusCount++ + paramName += strconv.Itoa(parser.plusCount) } segment := &routeSegment{ @@ -465,9 +487,9 @@ func splitNonEscaped(s, sep string) []string { } // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions -func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here +func (parser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here var i, paramsIterator, partLen int - for _, segment := range routeParser.segs { + for _, segment := range parser.segs { partLen = len(detectionPath) // check const segment if !segment.IsParam {