diff --git a/filters/builtin/compress.go b/filters/builtin/compress.go index 72a8a80a0d..0b33163aa2 100644 --- a/filters/builtin/compress.go +++ b/filters/builtin/compress.go @@ -13,6 +13,8 @@ import ( "sync" "github.com/andybalholm/brotli" + kpflate "github.com/klauspost/compress/flate" + kpgzip "github.com/klauspost/compress/gzip" log "github.com/sirupsen/logrus" "github.com/zalando/skipper/filters" @@ -342,12 +344,12 @@ func newEncoder(enc string, level int) (encoder, error) { if level > gzip.BestCompression { level = gzip.BestCompression } - return gzip.NewWriterLevel(nil, level) + return kpgzip.NewWriterLevel(nil, level) case "deflate": if level > flate.BestCompression { level = flate.BestCompression } - return flate.NewWriter(nil, level) + return kpflate.NewWriter(nil, level) default: unsupported() return nil, nil diff --git a/filters/builtin/compress_test.go b/filters/builtin/compress_test.go index b1c034ac62..f6e901e726 100644 --- a/filters/builtin/compress_test.go +++ b/filters/builtin/compress_test.go @@ -774,6 +774,12 @@ func TestPoolRelease(t *testing.T) { wg.Wait() } +func BenchmarkCompressDeflate0(b *testing.B) { benchmarkCompress(b, 0, []string{"deflate"}) } +func BenchmarkCompressDeflate2(b *testing.B) { benchmarkCompress(b, 100, []string{"deflate"}) } +func BenchmarkCompressDeflate4(b *testing.B) { benchmarkCompress(b, 10000, []string{"deflate"}) } +func BenchmarkCompressDeflate6(b *testing.B) { benchmarkCompress(b, 1000000, []string{"deflate"}) } +func BenchmarkCompressDeflate8(b *testing.B) { benchmarkCompress(b, 100000000, []string{"deflate"}) } + func BenchmarkCompressGzip0(b *testing.B) { benchmarkCompress(b, 0, []string{"gzip,deflate"}) } func BenchmarkCompressGzip2(b *testing.B) { benchmarkCompress(b, 100, []string{"gzip,deflate"}) } func BenchmarkCompressGzip4(b *testing.B) { benchmarkCompress(b, 10000, []string{"gzip,deflate"}) } @@ -786,6 +792,70 @@ func BenchmarkCompressBrotli4(b *testing.B) { benchmarkCompress(b, 10000, []stri func BenchmarkCompressBrotli6(b *testing.B) { benchmarkCompress(b, 1000000, []string{"br"}) } func BenchmarkCompressBrotli8(b *testing.B) { benchmarkCompress(b, 100000000, []string{"br"}) } +func benchmarkCompressJSON(b *testing.B, n int64, encoding []string) { + // Repeating JSON pattern — highly compressible, realistic payload + pattern := []byte(`{"id":12345,"name":"test-user","email":"user@example.com","active":true,"tags":["a","b"]},`) + buf := make([]byte, n) + for i := 0; i < len(buf); i += len(pattern) { + copy(buf[i:], pattern) + } + + s := NewCompress() + f, _ := s.CreateFilter(nil) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + body := io.NopCloser(bytes.NewReader(buf)) + req := &http.Request{Header: http.Header{"Accept-Encoding": encoding}} + rsp := &http.Response{ + Header: http.Header{"Content-Type": []string{"application/json"}}, + Body: body} + ctx := &filtertest.Context{ + FRequest: req, + FResponse: rsp} + f.Response(ctx) + _, err := io.ReadAll(rsp.Body) + if err != nil { + b.Fatal(err) + } + } + }) +} + +func benchmarkCompressLevel(b *testing.B, n int64, level int, encoding []string) { + s := NewCompress() + f, _ := s.CreateFilter([]any{float64(level)}) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + body := io.NopCloser(&io.LimitedReader{R: rand.New(rand.NewSource(0)), N: n}) + req := &http.Request{Header: http.Header{"Accept-Encoding": encoding}} + rsp := &http.Response{ + Header: http.Header{"Content-Type": []string{"application/octet-stream"}}, + Body: body} + ctx := &filtertest.Context{ + FRequest: req, + FResponse: rsp} + f.Response(ctx) + _, err := io.ReadAll(rsp.Body) + if err != nil { + b.Fatal(err) + } + } + }) +} + +// JSON payload benchmarks (compressible data) +func BenchmarkCompressGzipJSON4(b *testing.B) { benchmarkCompressJSON(b, 10000, []string{"gzip"}) } +func BenchmarkCompressGzipJSON6(b *testing.B) { benchmarkCompressJSON(b, 1000000, []string{"gzip"}) } +func BenchmarkCompressGzipJSON8(b *testing.B) { benchmarkCompressJSON(b, 100000000, []string{"gzip"}) } + +// Higher compression level (level 6 = DefaultCompression) +func BenchmarkCompressGzipLevel6_4(b *testing.B) { + benchmarkCompressLevel(b, 10000, 6, []string{"gzip"}) +} +func BenchmarkCompressGzipLevel6_6(b *testing.B) { + benchmarkCompressLevel(b, 1000000, 6, []string{"gzip"}) +} + func BenchmarkCanEncodeEntity(b *testing.B) { testCases := []struct { name string diff --git a/go.mod b/go.mod index ce8fe3f266..830353f13e 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.4 github.com/instana/go-sensor v1.73.1 + github.com/klauspost/compress v1.18.5 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.72 github.com/oklog/ulid v1.3.1 @@ -195,7 +196,6 @@ require ( github.com/itchyny/timefmt-go v0.1.7 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect diff --git a/go.sum b/go.sum index 7dc1e1f571..87c5fde31f 100644 --- a/go.sum +++ b/go.sum @@ -418,8 +418,8 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=