allocate skippedNodes from sync pool

This commit is contained in:
Zhu Xi 2021-10-09 22:41:34 +08:00
parent 2eb3ba8b52
commit 54f3b457c7
4 changed files with 38 additions and 28 deletions

View File

@ -55,8 +55,9 @@ type Context struct {
index int8 index int8
fullPath string fullPath string
engine *Engine engine *Engine
params *Params params *Params
skippedNodes *[]skippedNode
// This mutex protect Keys map // This mutex protect Keys map
mu sync.RWMutex mu sync.RWMutex
@ -99,6 +100,7 @@ func (c *Context) reset() {
c.queryCache = nil c.queryCache = nil
c.formCache = nil c.formCache = nil
*c.params = (*c.params)[:0] *c.params = (*c.params)[:0]
*c.skippedNodes = (*c.skippedNodes)[:0]
} }
// Copy returns a copy of the current context that can be safely used outside the request's scope. // Copy returns a copy of the current context that can be safely used outside the request's scope.

12
gin.go
View File

@ -147,6 +147,7 @@ type Engine struct {
pool sync.Pool pool sync.Pool
trees methodTrees trees methodTrees
maxParams uint16 maxParams uint16
maxSections uint16
trustedCIDRs []*net.IPNet trustedCIDRs []*net.IPNet
} }
@ -202,7 +203,8 @@ func Default() *Engine {
func (engine *Engine) allocateContext() *Context { func (engine *Engine) allocateContext() *Context {
v := make(Params, 0, engine.maxParams) v := make(Params, 0, engine.maxParams)
return &Context{engine: engine, params: &v} skippedNodes := make([]skippedNode, 0, engine.maxSections)
return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
} }
// Delims sets template left and right delims and returns a Engine instance. // Delims sets template left and right delims and returns a Engine instance.
@ -308,6 +310,10 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
if paramsCount := countParams(path); paramsCount > engine.maxParams { if paramsCount := countParams(path); paramsCount > engine.maxParams {
engine.maxParams = paramsCount engine.maxParams = paramsCount
} }
if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
engine.maxSections = sectionsCount
}
} }
// Routes returns a slice of registered routes, including some useful information, such as: // Routes returns a slice of registered routes, including some useful information, such as:
@ -529,7 +535,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
} }
root := t[i].root root := t[i].root
// Find route in tree // Find route in tree
value := root.getValue(rPath, c.params, unescape) value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.params != nil { if value.params != nil {
c.Params = *value.params c.Params = *value.params
} }
@ -557,7 +563,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
if tree.method == httpMethod { if tree.method == httpMethod {
continue continue
} }
if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil { if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
c.handlers = engine.allNoMethod c.handlers = engine.allNoMethod
serveError(c, http.StatusMethodNotAllowed, default405Body) serveError(c, http.StatusMethodNotAllowed, default405Body)
return return

31
tree.go
View File

@ -410,8 +410,8 @@ type skippedNode struct {
// If no handle can be found, a TSR (trailing slash redirect) recommendation is // If no handle can be found, a TSR (trailing slash redirect) recommendation is
// made if a handle exists with an extra (without the) trailing slash for the // made if a handle exists with an extra (without the) trailing slash for the
// given path. // given path.
func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
skippedNodes := make([]skippedNode, 0, countSections(path)) // Caching the latest nodes //skippedNodes := make([]skippedNode, 0, countSections(path)) // Caching the latest nodes
var globalParamsCount int16 var globalParamsCount int16
walk: // Outer loop for walking the tree walk: // Outer loop for walking the tree
@ -427,9 +427,9 @@ walk: // Outer loop for walking the tree
if c == idxc { if c == idxc {
// strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
if n.wildChild { if n.wildChild {
index := len(skippedNodes) index := len(*skippedNodes)
skippedNodes = skippedNodes[:index+1] *skippedNodes = (*skippedNodes)[:index+1]
skippedNodes[index] = skippedNode{ (*skippedNodes)[index] = skippedNode{
path: prefix + path, path: prefix + path,
node: &node{ node: &node{
path: n.path, path: n.path,
@ -464,10 +464,9 @@ walk: // Outer loop for walking the tree
*/ */
if path != "/" && !n.wildChild { if path != "/" && !n.wildChild {
for len(skippedNodes) > 0 { for l := len(*skippedNodes); l > 0; {
l := len(skippedNodes) skippedNode := (*skippedNodes)[l-1]
skippedNode := skippedNodes[l-1] *skippedNodes = (*skippedNodes)[:l-1]
skippedNodes = skippedNodes[:l-1]
if strings.HasSuffix(skippedNode.path, path) { if strings.HasSuffix(skippedNode.path, path) {
path = skippedNode.path path = skippedNode.path
n = skippedNode.node n = skippedNode.node
@ -593,10 +592,9 @@ walk: // Outer loop for walking the tree
// If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
// the current node needs to be equal to the latest matching node // the current node needs to be equal to the latest matching node
if n.handlers == nil && path != "/" { if n.handlers == nil && path != "/" {
for len(skippedNodes) > 0 { for l := len(*skippedNodes); l > 0; {
l := len(skippedNodes) skippedNode := (*skippedNodes)[l-1]
skippedNode := skippedNodes[l-1] *skippedNodes = (*skippedNodes)[:l-1]
skippedNodes = skippedNodes[:l-1]
if strings.HasSuffix(skippedNode.path, path) { if strings.HasSuffix(skippedNode.path, path) {
path = skippedNode.path path = skippedNode.path
n = skippedNode.node n = skippedNode.node
@ -639,10 +637,9 @@ walk: // Outer loop for walking the tree
} }
if path != "/" { if path != "/" {
for len(skippedNodes) > 0 { for l := len(*skippedNodes); l > 0; {
l := len(skippedNodes) skippedNode := (*skippedNodes)[l-1]
skippedNode := skippedNodes[l-1] *skippedNodes = (*skippedNodes)[:l-1]
skippedNodes = skippedNodes[:l-1]
if strings.HasSuffix(skippedNode.path, path) { if strings.HasSuffix(skippedNode.path, path) {
path = skippedNode.path path = skippedNode.path
n = skippedNode.node n = skippedNode.node

View File

@ -33,6 +33,11 @@ func getParams() *Params {
return &ps return &ps
} }
func getSkippedNodes() *[]skippedNode {
ps := make([]skippedNode, 0, 20)
return &ps
}
func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) { func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) {
unescape := false unescape := false
if len(unescapes) >= 1 { if len(unescapes) >= 1 {
@ -40,7 +45,7 @@ func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ..
} }
for _, request := range requests { for _, request := range requests {
value := tree.getValue(request.path, getParams(), unescape) value := tree.getValue(request.path, getParams(), getSkippedNodes(), unescape)
if value.handlers == nil { if value.handlers == nil {
if !request.nilHandler { if !request.nilHandler {
@ -605,7 +610,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/doc/", "/doc/",
} }
for _, route := range tsrRoutes { for _, route := range tsrRoutes {
value := tree.getValue(route, nil, false) value := tree.getValue(route, nil, getSkippedNodes(), false)
if value.handlers != nil { if value.handlers != nil {
t.Fatalf("non-nil handler for TSR route '%s", route) t.Fatalf("non-nil handler for TSR route '%s", route)
} else if !value.tsr { } else if !value.tsr {
@ -622,7 +627,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/api/world/abc", "/api/world/abc",
} }
for _, route := range noTsrRoutes { for _, route := range noTsrRoutes {
value := tree.getValue(route, nil, false) value := tree.getValue(route, nil, getSkippedNodes(), false)
if value.handlers != nil { if value.handlers != nil {
t.Fatalf("non-nil handler for No-TSR route '%s", route) t.Fatalf("non-nil handler for No-TSR route '%s", route)
} else if value.tsr { } else if value.tsr {
@ -641,7 +646,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) {
t.Fatalf("panic inserting test route: %v", recv) t.Fatalf("panic inserting test route: %v", recv)
} }
value := tree.getValue("/", nil, false) value := tree.getValue("/", nil, getSkippedNodes(), false)
if value.handlers != nil { if value.handlers != nil {
t.Fatalf("non-nil handler") t.Fatalf("non-nil handler")
} else if value.tsr { } else if value.tsr {
@ -821,7 +826,7 @@ func TestTreeInvalidNodeType(t *testing.T) {
// normal lookup // normal lookup
recv := catchPanic(func() { recv := catchPanic(func() {
tree.getValue("/test", nil, false) tree.getValue("/test", nil, getSkippedNodes(), false)
}) })
if rs, ok := recv.(string); !ok || rs != panicMsg { if rs, ok := recv.(string); !ok || rs != panicMsg {
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv) t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
@ -846,7 +851,7 @@ func TestTreeInvalidParamsType(t *testing.T) {
params := make(Params, 0) params := make(Params, 0)
// try to trigger slice bounds out of range with capacity 0 // try to trigger slice bounds out of range with capacity 0
tree.getValue("/test", &params, false) tree.getValue("/test", &params, getSkippedNodes(), false)
} }
func TestTreeWildcardConflictEx(t *testing.T) { func TestTreeWildcardConflictEx(t *testing.T) {