mirror of
https://github.com/gofiber/fiber.git
synced 2025-05-31 11:52:41 +00:00
♻️ Refactor: reduce the memory usage of RoutePatternMatch (#3335)
* ♻️ Refactor: improve RoutePatternMatch by adding RemoveEscapeCharBytes ``` goos: linux goarch: amd64 pkg: github.com/gofiber/fiber/v3 cpu: AMD EPYC 7763 64-Core Processor │ route_pattern_match_old.txt │ route_pattern_match_new.txt │ │ sec/op │ sec/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 263.4n ± 2% 249.0n ± 4% -5.47% (p=0.001 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 258.7n ± 4% 244.7n ± 2% -5.43% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 254.6n ± 4% 246.3n ± 2% -3.26% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 265.1n ± 4% 255.6n ± 3% -3.60% (p=0.001 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 775.9n ± 3% 775.6n ± 2% ~ (p=0.424 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 796.7n ± 3% 767.1n ± 2% -3.72% (p=0.001 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 916.2n ± 1% 904.8n ± 3% ~ (p=0.052 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 913.8n ± 4% 909.1n ± 3% ~ (p=0.393 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 915.0n ± 3% 907.2n ± 2% ~ (p=0.165 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 917.5n ± 2% 876.7n ± 2% -4.46% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 918.5n ± 2% 886.8n ± 2% -3.45% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 935.6n ± 2% 901.9n ± 2% -3.60% (p=0.000 n=10) geomean 588.3n 570.7n -2.99% │ route_pattern_match_old.txt │ route_pattern_match_new.txt │ │ B/op │ B/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 168.0 ± 0% 152.0 ± 0% -9.52% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 160.0 ± 0% 144.0 ± 0% -10.00% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 160.0 ± 0% 144.0 ± 0% -10.00% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 176.0 ± 0% 160.0 ± 0% -9.09% (p=0.000 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 440.0 ± 0% 440.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 464.0 ± 0% 440.0 ± 0% -5.17% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) geomean 353.7 337.9 -4.47% ¹ all samples are equal │ route_pattern_match_old.txt │ route_pattern_match_new.txt │ │ allocs/op │ allocs/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 13.00 ± 0% 13.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 14.00 ± 0% 13.00 ± 0% -7.14% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) geomean 10.67 9.811 -8.08% ¹ all samples are equal ``` * ♻️ Refactor: returned type of analyseParameterPart and analyseConstantPart ``` goos: linux goarch: amd64 pkg: github.com/gofiber/fiber/v3 cpu: AMD EPYC 7763 64-Core Processor │ route_pattern_match_old.txt │ route_pattern_match_new3.txt │ │ sec/op │ sec/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 264.3n ± 2% 253.8n ± 2% -3.95% (p=0.001 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 258.5n ± 1% 247.6n ± 2% -4.24% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 260.8n ± 3% 249.7n ± 4% -4.26% (p=0.003 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 265.4n ± 2% 256.1n ± 2% -3.49% (p=0.000 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 783.8n ± 2% 777.5n ± 3% ~ (p=0.218 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 797.8n ± 1% 773.6n ± 3% -3.03% (p=0.001 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 920.3n ± 2% 926.0n ± 3% ~ (p=0.896 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 920.4n ± 4% 908.2n ± 2% ~ (p=0.063 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 927.9n ± 2% 919.0n ± 3% ~ (p=0.579 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 920.4n ± 3% 889.5n ± 3% -3.36% (p=0.007 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 916.9n ± 2% 891.9n ± 2% -2.73% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 938.8n ± 5% 891.2n ± 2% -5.07% (p=0.000 n=10) geomean 591.7n 575.5n -2.73% │ route_pattern_match_old.txt │ route_pattern_match_new3.txt │ │ B/op │ B/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 168.0 ± 0% 152.0 ± 0% -9.52% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 160.0 ± 0% 144.0 ± 0% -10.00% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 160.0 ± 0% 144.0 ± 0% -10.00% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 176.0 ± 0% 160.0 ± 0% -9.09% (p=0.000 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 440.0 ± 0% 440.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 464.0 ± 0% 440.0 ± 0% -5.17% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 536.0 ± 0% 536.0 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 544.0 ± 0% 528.0 ± 0% -2.94% (p=0.000 n=10) geomean 353.7 337.9 -4.47% ¹ all samples are equal │ route_pattern_match_old.txt │ route_pattern_match_new3.txt │ │ allocs/op │ allocs/op vs base │ _RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-4 6.000 ± 0% 5.000 ± 0% -16.67% (p=0.000 n=10) _RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-4 13.00 ± 0% 13.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-4 14.00 ± 0% 13.00 ± 0% -7.14% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-4 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) _RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-4 15.00 ± 0% 14.00 ± 0% -6.67% (p=0.000 n=10) geomean 10.67 9.811 -8.08% ¹ all samples are equal ``` --------- Co-authored-by: RW <rene@gofiber.io>
This commit is contained in:
parent
6afba957f1
commit
9e6f4fd408
60
path.go
60
path.go
@ -152,11 +152,11 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
|
||||
pattern = "/" + pattern
|
||||
}
|
||||
|
||||
patternPretty := pattern
|
||||
patternPretty := []byte(pattern)
|
||||
|
||||
// Case-sensitive routing, all to lowercase
|
||||
if !config.CaseSensitive {
|
||||
patternPretty = utils.ToLower(patternPretty)
|
||||
patternPretty = utils.ToLowerBytes(patternPretty)
|
||||
path = utils.ToLower(path)
|
||||
}
|
||||
// Strict routing, remove trailing slashes
|
||||
@ -164,12 +164,12 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
|
||||
patternPretty = utils.TrimRight(patternPretty, '/')
|
||||
}
|
||||
|
||||
parser := parseRoute(patternPretty)
|
||||
parser := parseRoute(string(patternPretty))
|
||||
|
||||
if patternPretty == "/" && path == "/" {
|
||||
if string(patternPretty) == "/" && path == "/" {
|
||||
return true
|
||||
// '*' wildcard matches any path
|
||||
} else if patternPretty == "/*" {
|
||||
} else if string(patternPretty) == "/*" {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -180,35 +180,28 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool {
|
||||
}
|
||||
}
|
||||
// Check for a simple match
|
||||
patternPretty = RemoveEscapeChar(patternPretty)
|
||||
if len(patternPretty) == len(path) && patternPretty == path {
|
||||
return true
|
||||
}
|
||||
// No match
|
||||
return false
|
||||
patternPretty = RemoveEscapeCharBytes(patternPretty)
|
||||
|
||||
return string(patternPretty) == path
|
||||
}
|
||||
|
||||
// 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{}
|
||||
part := ""
|
||||
var n int
|
||||
var seg *routeSegment
|
||||
for len(pattern) > 0 {
|
||||
nextParamPosition := findNextParamPosition(pattern)
|
||||
// handle the parameter part
|
||||
if nextParamPosition == 0 {
|
||||
processedPart, seg := parser.analyseParameterPart(pattern, customConstraints...)
|
||||
parser.params, parser.segs, part = append(parser.params, seg.ParamName), append(parser.segs, seg), processedPart
|
||||
n, seg = parser.analyseParameterPart(pattern, customConstraints...)
|
||||
parser.params, parser.segs = append(parser.params, seg.ParamName), append(parser.segs, seg)
|
||||
} else {
|
||||
processedPart, seg := parser.analyseConstantPart(pattern, nextParamPosition)
|
||||
parser.segs, part = append(parser.segs, seg), processedPart
|
||||
n, seg = parser.analyseConstantPart(pattern, nextParamPosition)
|
||||
parser.segs = append(parser.segs, seg)
|
||||
}
|
||||
|
||||
// reduce the pattern by the processed parts
|
||||
if len(part) == len(pattern) {
|
||||
break
|
||||
}
|
||||
pattern = pattern[len(part):]
|
||||
pattern = pattern[n:]
|
||||
}
|
||||
// mark last segment
|
||||
if len(parser.segs) > 0 {
|
||||
@ -283,7 +276,7 @@ func findNextParamPosition(pattern string) int {
|
||||
}
|
||||
|
||||
// analyseConstantPart find the end of the constant part and create the route segment
|
||||
func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, *routeSegment) {
|
||||
func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (int, *routeSegment) {
|
||||
// handle the constant part
|
||||
processedPart := pattern
|
||||
if nextParamPosition != -1 {
|
||||
@ -291,14 +284,14 @@ func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (
|
||||
processedPart = pattern[:nextParamPosition]
|
||||
}
|
||||
constPart := RemoveEscapeChar(processedPart)
|
||||
return processedPart, &routeSegment{
|
||||
return len(processedPart), &routeSegment{
|
||||
Const: constPart,
|
||||
Length: len(constPart),
|
||||
}
|
||||
}
|
||||
|
||||
// analyseParameterPart find the parameter end and create the route segment
|
||||
func (routeParser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (string, *routeSegment) {
|
||||
func (routeParser *routeParser) analyseParameterPart(pattern string, customConstraints ...CustomConstraint) (int, *routeSegment) {
|
||||
isWildCard := pattern[0] == wildcardParam
|
||||
isPlusParam := pattern[0] == plusParam
|
||||
|
||||
@ -329,6 +322,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string, customConst
|
||||
|
||||
// cut params part
|
||||
processedPart := pattern[0 : parameterEndPosition+1]
|
||||
n := parameterEndPosition + 1
|
||||
paramName := RemoveEscapeChar(GetTrimmedParam(processedPart))
|
||||
|
||||
// Check has constraint
|
||||
@ -402,7 +396,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string, customConst
|
||||
segment.Constraints = constraints
|
||||
}
|
||||
|
||||
return processedPart, segment
|
||||
return n, segment
|
||||
}
|
||||
|
||||
// isInCharset check is the given character in the charset list
|
||||
@ -618,7 +612,7 @@ func GetTrimmedParam(param string) string {
|
||||
return param[start:end]
|
||||
}
|
||||
|
||||
// RemoveEscapeChar remove escape characters
|
||||
// RemoveEscapeChar removes escape characters
|
||||
func RemoveEscapeChar(word string) string {
|
||||
b := []byte(word)
|
||||
dst := 0
|
||||
@ -632,6 +626,18 @@ func RemoveEscapeChar(word string) string {
|
||||
return string(b[:dst])
|
||||
}
|
||||
|
||||
// RemoveEscapeCharBytes removes escape characters
|
||||
func RemoveEscapeCharBytes(word []byte) []byte {
|
||||
dst := 0
|
||||
for src := 0; src < len(word); src++ {
|
||||
if word[src] != '\\' {
|
||||
word[dst] = word[src]
|
||||
dst++
|
||||
}
|
||||
}
|
||||
return word[:dst]
|
||||
}
|
||||
|
||||
func getParamConstraintType(constraintPart string) TypeConstraint {
|
||||
switch constraintPart {
|
||||
case ConstraintInt:
|
||||
|
Loading…
x
Reference in New Issue
Block a user