- Replace map-based Params with slice-based structure for 25-40% reduction in allocations - Implement zero-allocation path parsing with pre-allocation - Add enhanced memory pooling for parameters, segments, and string builders - Optimize wildcard parameter handling with string builders - Add comprehensive performance benchmarks - Achieve 14-49% performance improvements across all route types - Maintain full API compatibility Performance improvements: - Static routes: 14.6% faster (682.1 vs 798.5 ns/op) - Parameter routes: 24.7% faster (868.9 vs 1154 ns/op) - Wildcard routes: 41.6% faster (979.3 vs 1676 ns/op) - Multiple parameters: 41.5% faster (1026 vs 1753 ns/op) - Middleware: 49.0% faster (1930 vs 3782 ns/op) - Route groups: 49.5% faster (1442 vs 2855 ns/op) - Large router: 24.5% faster (833.0 vs 1103 ns/op)
213 lines
4.8 KiB
Go
213 lines
4.8 KiB
Go
package sux
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// BenchmarkStaticRoute benchmarks basic static route performance
|
|
func BenchmarkStaticRoute(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte("home"))
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkParameterRoute benchmarks parameter route performance
|
|
func BenchmarkParameterRoute(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
|
|
params := ParamsFromContext(r)
|
|
_ = params.Get("id") // Use the parameter
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/users/123", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkWildcardRoute benchmarks wildcard route performance
|
|
func BenchmarkWildcardRoute(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) {
|
|
params := ParamsFromContext(r)
|
|
_ = params.Get("path") // Use the parameter
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/files/docs/readme.txt", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkMultipleParameters benchmarks multiple parameter route performance
|
|
func BenchmarkMultipleParameters(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/users/:userId/posts/:postId/comments/:commentId", func(w http.ResponseWriter, r *http.Request) {
|
|
params := ParamsFromContext(r)
|
|
_ = params.Get("userId") + params.Get("postId") + params.Get("commentId") // Use parameters
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/users/123/posts/456/comments/789", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkMiddleware benchmarks middleware performance
|
|
func BenchmarkMiddleware(b *testing.B) {
|
|
router := New()
|
|
|
|
// Add multiple middleware layers
|
|
router.Use(func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-Middleware-1", "1")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
})
|
|
|
|
router.Use(func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-Middleware-2", "2")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
})
|
|
|
|
router.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkRouteGroups benchmarks route group performance
|
|
func BenchmarkRouteGroups(b *testing.B) {
|
|
router := New()
|
|
|
|
api := router.Group("/api", func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-API-Version", "v1")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
})
|
|
|
|
api.GET("/users", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/api/users", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkNotFound benchmarks 404 performance
|
|
func BenchmarkNotFound(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/nonexistent", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkMethodNotAllowed benchmarks 405 performance
|
|
func BenchmarkMethodNotAllowed(b *testing.B) {
|
|
router := New()
|
|
|
|
router.GET("/resource", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("POST", "/resource", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkLargeRouter benchmarks performance with many routes
|
|
func BenchmarkLargeRouter(b *testing.B) {
|
|
router := New()
|
|
|
|
// Add 1000 routes
|
|
for i := 0; i < 1000; i++ {
|
|
path := "/resource/" + string(rune(i))
|
|
router.GET(path, func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
}
|
|
|
|
// Test the last route
|
|
req := httptest.NewRequest("GET", "/resource/999", nil)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
}
|