RadixTree.searchLongest previously allocated &MatchResult{} on the
heap every time it encountered a handler-bearing node during tree
traversal — potentially N allocations per lookup with only 1 surviving.
Changes:
- searchLongest now tracks best *RadixNode (stack pointer) instead of
allocating MatchResult at every handler node
- FindLongestPrefix allocates a single pooled MatchResult via sync.Pool
only when returning a match
- Add ReleaseMatchResult() for callers to return MatchResult to pool
- LocationEngine.Match releases pooled results after use
Result: 0 B/op, 0 allocs/op on tree traversal benchmarks (sequential
and parallel).
44 lines
1.1 KiB
Go
44 lines
1.1 KiB
Go
package matcher
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
func BenchmarkRadixTreeFindLongestPrefix(b *testing.B) {
|
|
tree := NewRadixTree()
|
|
paths := []string{"/", "/api", "/api/v1", "/api/v1/users", "/static", "/static/css", "/static/js", "/health", "/favicon.ico"}
|
|
dummyHandler := func(ctx *fasthttp.RequestCtx) {}
|
|
for _, p := range paths {
|
|
tree.Insert(p, dummyHandler, 0, LocationTypePrefix, false)
|
|
}
|
|
tree.MarkInitialized()
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
result := tree.FindLongestPrefix("/api/v1/users")
|
|
ReleaseMatchResult(result)
|
|
}
|
|
}
|
|
|
|
func BenchmarkRadixTreeFindLongestPrefixParallel(b *testing.B) {
|
|
tree := NewRadixTree()
|
|
paths := []string{"/", "/api", "/api/v1", "/api/v1/users", "/static", "/static/css", "/static/js", "/health", "/favicon.ico"}
|
|
dummyHandler := func(ctx *fasthttp.RequestCtx) {}
|
|
for _, p := range paths {
|
|
tree.Insert(p, dummyHandler, 0, LocationTypePrefix, false)
|
|
}
|
|
tree.MarkInitialized()
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
result := tree.FindLongestPrefix("/api/v1/users")
|
|
ReleaseMatchResult(result)
|
|
}
|
|
})
|
|
}
|