Files
sux/benchmark_test.go
Darko Luketic 045ac8b5a3 Implement performance optimizations v1.1.0
- 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)
2025-10-26 18:06:15 +01:00

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)
}
}