mirror of https://github.com/gofiber/fiber.git
♻️ 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 problempull/3348/head
parent
600ebd95ce
commit
1b26cf6b5e
42
path.go
42
path.go
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue