200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
|
|
package sux
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"runtime"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
// BenchmarkParameterOperations benchmarks the new parameter handling
|
||
|
|
func BenchmarkParameterOperations(b *testing.B) {
|
||
|
|
params := Params{
|
||
|
|
keys: make([]string, 0, 4),
|
||
|
|
values: make([]string, 0, 4),
|
||
|
|
}
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
params.Set("id", "123")
|
||
|
|
params.Set("name", "test")
|
||
|
|
_ = params.Get("id")
|
||
|
|
_ = params.Get("name")
|
||
|
|
params.Reset()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkPathParsing compares old vs new path parsing
|
||
|
|
func BenchmarkPathParsingOld(b *testing.B) {
|
||
|
|
path := "/users/123/posts/456/comments/789"
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
_ = parsePath(path)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func BenchmarkPathParsingNew(b *testing.B) {
|
||
|
|
path := "/users/123/posts/456/comments/789"
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
_ = parsePathZeroAlloc(path)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkMemoryUsage tests memory efficiency under load
|
||
|
|
func BenchmarkMemoryUsage(b *testing.B) {
|
||
|
|
router := New()
|
||
|
|
|
||
|
|
// Add routes with different patterns
|
||
|
|
router.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
router.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("id")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
router.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("path")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
|
||
|
|
requests := []struct {
|
||
|
|
method string
|
||
|
|
path string
|
||
|
|
}{
|
||
|
|
{"GET", "/"},
|
||
|
|
{"GET", "/users/123"},
|
||
|
|
{"GET", "/files/docs/readme.txt"},
|
||
|
|
}
|
||
|
|
|
||
|
|
var reqs []*http.Request
|
||
|
|
for _, req := range requests {
|
||
|
|
for i := 0; i < 100; i++ {
|
||
|
|
reqs = append(reqs, httptest.NewRequest(req.method, req.path, nil))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(w, reqs[i%len(reqs)])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkConcurrentAccess tests performance under high concurrency
|
||
|
|
func BenchmarkConcurrentAccess(b *testing.B) {
|
||
|
|
router := New()
|
||
|
|
router.GET("/test/:id", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("id")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
|
||
|
|
req := httptest.NewRequest("GET", "/test/123", nil)
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
b.RunParallel(func(pb *testing.PB) {
|
||
|
|
for pb.Next() {
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(w, req)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkGCPressure measures GC pressure from allocations
|
||
|
|
func BenchmarkGCPressure(b *testing.B) {
|
||
|
|
router := New()
|
||
|
|
router.GET("/users/:id/posts/:postId", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("id") + params.Get("postId")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
|
||
|
|
req := httptest.NewRequest("GET", "/users/123/posts/456", nil)
|
||
|
|
|
||
|
|
// Force GC before benchmark
|
||
|
|
runtime.GC()
|
||
|
|
|
||
|
|
var m1, m2 runtime.MemStats
|
||
|
|
runtime.ReadMemStats(&m1)
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(w, req)
|
||
|
|
}
|
||
|
|
|
||
|
|
b.StopTimer()
|
||
|
|
runtime.ReadMemStats(&m2)
|
||
|
|
b.Logf("Allocs: %d, TotalAlloc: %d bytes", m2.Mallocs-m1.Mallocs, m2.TotalAlloc-m1.TotalAlloc)
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkWildcardOptimization tests the wildcard parameter optimization
|
||
|
|
func BenchmarkWildcardOptimization(b *testing.B) {
|
||
|
|
router := New()
|
||
|
|
router.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("path")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
|
||
|
|
// Test with varying path lengths
|
||
|
|
paths := []string{
|
||
|
|
"/files/file.txt",
|
||
|
|
"/files/docs/readme.txt",
|
||
|
|
"/files/docs/api/v1/users.json",
|
||
|
|
"/files/very/deep/nested/path/with/many/segments/file.txt",
|
||
|
|
}
|
||
|
|
|
||
|
|
var reqs []*http.Request
|
||
|
|
for _, path := range paths {
|
||
|
|
for i := 0; i < 25; i++ {
|
||
|
|
reqs = append(reqs, httptest.NewRequest("GET", path, nil))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(w, reqs[i%len(reqs)])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BenchmarkPoolEfficiency tests the efficiency of our pooling strategy
|
||
|
|
func BenchmarkPoolEfficiency(b *testing.B) {
|
||
|
|
router := New()
|
||
|
|
router.GET("/test/:param", func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
params := ParamsFromContext(r)
|
||
|
|
_ = params.Get("param")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
})
|
||
|
|
|
||
|
|
req := httptest.NewRequest("GET", "/test/value", nil)
|
||
|
|
|
||
|
|
b.ResetTimer()
|
||
|
|
b.ReportAllocs()
|
||
|
|
|
||
|
|
// This should show minimal allocations due to pooling
|
||
|
|
for i := 0; i < b.N; i++ {
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(w, req)
|
||
|
|
}
|
||
|
|
}
|