♻️ 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
pull/3348/head
Kashiwa 2025-03-10 16:04:04 +08:00 committed by GitHub
parent 600ebd95ce
commit 1b26cf6b5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 32 additions and 10 deletions

42
path.go
View File

@ -11,6 +11,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"unicode" "unicode"
@ -26,6 +27,12 @@ type routeParser struct {
plusCount int // number of plus parameters, used internally to give the plus parameter its number 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 // routeSegment holds the segment metadata
type routeSegment struct { type routeSegment struct {
// const information // const information
@ -163,7 +170,10 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
patternPretty = utils.TrimRight(patternPretty, '/') 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 == "/" { if string(patternPretty) == "/" && path == "/" {
return true return true
@ -184,10 +194,16 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
return string(patternPretty) == path 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, // 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 // this information is needed later when assigning the requests to the declared routes
func parseRoute(pattern string, customConstraints ...CustomConstraint) routeParser { func (parser *routeParser) parseRoute(pattern string, customConstraints ...CustomConstraint) {
parser := routeParser{}
var n int var n int
var seg *routeSegment var seg *routeSegment
for len(pattern) > 0 { 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[len(parser.segs)-1].IsLast = true
} }
parser.segs = addParameterMetaInfo(parser.segs) 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 return parser
} }
@ -290,7 +312,7 @@ func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (
} }
// analyseParameterPart find the parameter end and create the route segment // 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 isWildCard := pattern[0] == wildcardParam
isPlusParam := pattern[0] == plusParam isPlusParam := pattern[0] == plusParam
@ -377,11 +399,11 @@ func (routeParser *routeParser) analyseParameterPart(pattern string, customConst
// add access iterator to wildcard and plus // add access iterator to wildcard and plus
if isWildCard { if isWildCard {
routeParser.wildCardCount++ parser.wildCardCount++
paramName += strconv.Itoa(routeParser.wildCardCount) paramName += strconv.Itoa(parser.wildCardCount)
} else if isPlusParam { } else if isPlusParam {
routeParser.plusCount++ parser.plusCount++
paramName += strconv.Itoa(routeParser.plusCount) paramName += strconv.Itoa(parser.plusCount)
} }
segment := &routeSegment{ 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 // 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 var i, paramsIterator, partLen int
for _, segment := range routeParser.segs { for _, segment := range parser.segs {
partLen = len(detectionPath) partLen = len(detectionPath)
// check const segment // check const segment
if !segment.IsParam { if !segment.IsParam {