Skip to content

Commit 4602335

Browse files
authored
Merge pull request #1689 from pafuent/routing_misses_performance_improvements
Improve router performance with dedicated child types
2 parents 4422e3b + 045bec5 commit 4602335

File tree

2 files changed

+271
-87
lines changed

2 files changed

+271
-87
lines changed

router.go

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ type (
1414
echo *Echo
1515
}
1616
node struct {
17-
kind kind
18-
label byte
19-
prefix string
20-
parent *node
21-
children children
22-
ppath string
23-
pnames []string
24-
methodHandler *methodHandler
17+
kind kind
18+
label byte
19+
prefix string
20+
parent *node
21+
staticChildrens children
22+
ppath string
23+
pnames []string
24+
methodHandler *methodHandler
25+
paramChildren *node
26+
anyChildren *node
2527
}
2628
kind uint8
2729
children []*node
@@ -44,6 +46,9 @@ const (
4446
skind kind = iota
4547
pkind
4648
akind
49+
50+
paramLabel = byte(':')
51+
anyLabel = byte('*')
4752
)
4853

4954
// NewRouter returns a new Router instance.
@@ -134,23 +139,32 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
134139
}
135140
} else if l < pl {
136141
// Split node
137-
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
142+
n := newNode(cn.kind, cn.prefix[l:], cn, cn.staticChildrens, cn.methodHandler, cn.ppath, cn.pnames, cn.paramChildren, cn.anyChildren)
138143

139144
// Update parent path for all children to new node
140-
for _, child := range cn.children {
145+
for _, child := range cn.staticChildrens {
141146
child.parent = n
142147
}
148+
if cn.paramChildren != nil {
149+
cn.paramChildren.parent = n
150+
}
151+
if cn.anyChildren != nil {
152+
cn.anyChildren.parent = n
153+
}
143154

144155
// Reset parent node
145156
cn.kind = skind
146157
cn.label = cn.prefix[0]
147158
cn.prefix = cn.prefix[:l]
148-
cn.children = nil
159+
cn.staticChildrens = nil
149160
cn.methodHandler = new(methodHandler)
150161
cn.ppath = ""
151162
cn.pnames = nil
163+
cn.paramChildren = nil
164+
cn.anyChildren = nil
152165

153-
cn.addChild(n)
166+
// Only Static children could reach here
167+
cn.addStaticChild(n)
154168

155169
if l == sl {
156170
// At parent node
@@ -160,9 +174,10 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
160174
cn.pnames = pnames
161175
} else {
162176
// Create child node
163-
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
177+
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames, nil, nil)
164178
n.addHandler(method, h)
165-
cn.addChild(n)
179+
// Only Static children could reach here
180+
cn.addStaticChild(n)
166181
}
167182
} else if l < sl {
168183
search = search[l:]
@@ -173,9 +188,16 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
173188
continue
174189
}
175190
// Create child node
176-
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
191+
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames, nil, nil)
177192
n.addHandler(method, h)
178-
cn.addChild(n)
193+
switch t {
194+
case skind:
195+
cn.addStaticChild(n)
196+
case pkind:
197+
cn.paramChildren = n
198+
case akind:
199+
cn.anyChildren = n
200+
}
179201
} else {
180202
// Node already exists
181203
if h != nil {
@@ -190,46 +212,45 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
190212
}
191213
}
192214

193-
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
215+
func newNode(t kind, pre string, p *node, sc children, mh *methodHandler, ppath string, pnames []string, paramChildren, anyChildren *node) *node {
194216
return &node{
195-
kind: t,
196-
label: pre[0],
197-
prefix: pre,
198-
parent: p,
199-
children: c,
200-
ppath: ppath,
201-
pnames: pnames,
202-
methodHandler: mh,
217+
kind: t,
218+
label: pre[0],
219+
prefix: pre,
220+
parent: p,
221+
staticChildrens: sc,
222+
ppath: ppath,
223+
pnames: pnames,
224+
methodHandler: mh,
225+
paramChildren: paramChildren,
226+
anyChildren: anyChildren,
203227
}
204228
}
205229

206-
func (n *node) addChild(c *node) {
207-
n.children = append(n.children, c)
230+
func (n *node) addStaticChild(c *node) {
231+
n.staticChildrens = append(n.staticChildrens, c)
208232
}
209233

210-
func (n *node) findChild(l byte, t kind) *node {
211-
for _, c := range n.children {
212-
if c.label == l && c.kind == t {
234+
func (n *node) findStaticChild(l byte) *node {
235+
for _, c := range n.staticChildrens {
236+
if c.label == l {
213237
return c
214238
}
215239
}
216240
return nil
217241
}
218242

219243
func (n *node) findChildWithLabel(l byte) *node {
220-
for _, c := range n.children {
244+
for _, c := range n.staticChildrens {
221245
if c.label == l {
222246
return c
223247
}
224248
}
225-
return nil
226-
}
227-
228-
func (n *node) findChildByKind(t kind) *node {
229-
for _, c := range n.children {
230-
if c.kind == t {
231-
return c
232-
}
249+
if l == paramLabel {
250+
return n.paramChildren
251+
}
252+
if l == anyLabel {
253+
return n.anyChildren
233254
}
234255
return nil
235256
}
@@ -356,7 +377,7 @@ func (r *Router) Find(method, path string, c Context) {
356377
// Attempt to go back up the tree on no matching prefix or no remaining search
357378
if l != pl || search == "" {
358379
// Handle special case of trailing slash route with existing any route (see #1526)
359-
if path[len(path)-1] == '/' && cn.findChildByKind(akind) != nil {
380+
if path[len(path)-1] == '/' && cn.anyChildren != nil {
360381
goto Any
361382
}
362383
if nn == nil { // Issue #1348
@@ -372,7 +393,7 @@ func (r *Router) Find(method, path string, c Context) {
372393
}
373394

374395
// Static node
375-
if child = cn.findChild(search[0], skind); child != nil {
396+
if child = cn.findStaticChild(search[0]); child != nil {
376397
// Save next
377398
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
378399
nk = pkind
@@ -385,7 +406,7 @@ func (r *Router) Find(method, path string, c Context) {
385406

386407
Param:
387408
// Param node
388-
if child = cn.findChildByKind(pkind); child != nil {
409+
if child = cn.paramChildren; child != nil {
389410
// Issue #378
390411
if len(pvalues) == n {
391412
continue
@@ -410,7 +431,7 @@ func (r *Router) Find(method, path string, c Context) {
410431

411432
Any:
412433
// Any node
413-
if cn = cn.findChildByKind(akind); cn != nil {
434+
if cn = cn.anyChildren; cn != nil {
414435
// If any node is found, use remaining path for pvalues
415436
pvalues[len(cn.pnames)-1] = search
416437
break
@@ -424,7 +445,7 @@ func (r *Router) Find(method, path string, c Context) {
424445
search = ns
425446
np := nn.parent
426447
// Consider param route one level up only
427-
if cn = nn.findChildByKind(pkind); cn != nil {
448+
if cn = nn.paramChildren; cn != nil {
428449
pos := strings.IndexByte(ns, '/')
429450
if pos == -1 {
430451
// If no slash is remaining in search string set param value
@@ -443,7 +464,7 @@ func (r *Router) Find(method, path string, c Context) {
443464
// No param route found, try to resolve nearest any route
444465
for {
445466
np = nn.parent
446-
if cn = nn.findChildByKind(akind); cn != nil {
467+
if cn = nn.anyChildren; cn != nil {
447468
break
448469
}
449470
if np == nil {
@@ -474,7 +495,7 @@ func (r *Router) Find(method, path string, c Context) {
474495

475496
// Dig further for any, might have an empty value for *, e.g.
476497
// serving a directory. Issue #207.
477-
if cn = cn.findChildByKind(akind); cn == nil {
498+
if cn = cn.anyChildren; cn == nil {
478499
return
479500
}
480501
if h := cn.findHandler(method); h != nil {

0 commit comments

Comments
 (0)