Skip to content

Commit 1b26cf6

Browse files
authored
♻️ Refactor: optimize routeParser by using sync.Pool (#3343)
* ♻️ 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
1 parent 600ebd9 commit 1b26cf6

File tree

1 file changed

+32
-10
lines changed

1 file changed

+32
-10
lines changed

path.go

+32-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"regexp"
1212
"strconv"
1313
"strings"
14+
"sync"
1415
"time"
1516
"unicode"
1617

@@ -26,6 +27,12 @@ type routeParser struct {
2627
plusCount int // number of plus parameters, used internally to give the plus parameter its number
2728
}
2829

30+
var routerParserPool = &sync.Pool{
31+
New: func() any {
32+
return &routeParser{}
33+
},
34+
}
35+
2936
// routeSegment holds the segment metadata
3037
type routeSegment struct {
3138
// const information
@@ -163,7 +170,10 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
163170
patternPretty = utils.TrimRight(patternPretty, '/')
164171
}
165172

166-
parser := parseRoute(string(patternPretty))
173+
parser, _ := routerParserPool.Get().(*routeParser) //nolint:errcheck // only contains routeParser
174+
parser.reset()
175+
parser.parseRoute(string(patternPretty))
176+
defer routerParserPool.Put(parser)
167177

168178
if string(patternPretty) == "/" && path == "/" {
169179
return true
@@ -184,10 +194,16 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
184194
return string(patternPretty) == path
185195
}
186196

197+
func (parser *routeParser) reset() {
198+
parser.segs = parser.segs[:0]
199+
parser.params = parser.params[:0]
200+
parser.wildCardCount = 0
201+
parser.plusCount = 0
202+
}
203+
187204
// parseRoute analyzes the route and divides it into segments for constant areas and parameters,
188205
// this information is needed later when assigning the requests to the declared routes
189-
func parseRoute(pattern string, customConstraints ...CustomConstraint) routeParser {
190-
parser := routeParser{}
206+
func (parser *routeParser) parseRoute(pattern string, customConstraints ...CustomConstraint) {
191207
var n int
192208
var seg *routeSegment
193209
for len(pattern) > 0 {
@@ -207,7 +223,13 @@ func parseRoute(pattern string, customConstraints ...CustomConstraint) routePars
207223
parser.segs[len(parser.segs)-1].IsLast = true
208224
}
209225
parser.segs = addParameterMetaInfo(parser.segs)
226+
}
210227

228+
// parseRoute analyzes the route and divides it into segments for constant areas and parameters,
229+
// this information is needed later when assigning the requests to the declared routes
230+
func parseRoute(pattern string, customConstraints ...CustomConstraint) routeParser {
231+
parser := routeParser{}
232+
parser.parseRoute(pattern, customConstraints...)
211233
return parser
212234
}
213235

@@ -290,7 +312,7 @@ func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (
290312
}
291313

292314
// analyseParameterPart find the parameter end and create the route segment
293-
func (routeParser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (int, *routeSegment) {
315+
func (parser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (int, *routeSegment) {
294316
isWildCard := pattern[0] == wildcardParam
295317
isPlusParam := pattern[0] == plusParam
296318

@@ -377,11 +399,11 @@ func (routeParser *routeParser) analyseParameterPart(pattern string, customConst
377399

378400
// add access iterator to wildcard and plus
379401
if isWildCard {
380-
routeParser.wildCardCount++
381-
paramName += strconv.Itoa(routeParser.wildCardCount)
402+
parser.wildCardCount++
403+
paramName += strconv.Itoa(parser.wildCardCount)
382404
} else if isPlusParam {
383-
routeParser.plusCount++
384-
paramName += strconv.Itoa(routeParser.plusCount)
405+
parser.plusCount++
406+
paramName += strconv.Itoa(parser.plusCount)
385407
}
386408

387409
segment := &routeSegment{
@@ -465,9 +487,9 @@ func splitNonEscaped(s, sep string) []string {
465487
}
466488

467489
// getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions
468-
func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here
490+
func (parser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here
469491
var i, paramsIterator, partLen int
470-
for _, segment := range routeParser.segs {
492+
for _, segment := range parser.segs {
471493
partLen = len(detectionPath)
472494
// check const segment
473495
if !segment.IsParam {

0 commit comments

Comments
 (0)