Created
September 26, 2025 09:49
-
-
Save florianl/22b50787ce38f0d637df17ed7875be42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| From 39729efd92ad61b13d6b4db1b49f1eb521e5b7ed Mon Sep 17 00:00:00 2001 | |
| From: Florian Lehner <florian.lehner@elastic.co> | |
| Date: Fri, 26 Sep 2025 11:47:49 +0200 | |
| Subject: [PATCH] benchmark-confighttp-syncPool | |
| Signed-off-by: Florian Lehner <florian.lehner@elastic.co> | |
| --- | |
| config/confighttp/compression_test.go | 181 ++++++++++++++++++++++++++ | |
| 1 file changed, 181 insertions(+) | |
| diff --git a/config/confighttp/compression_test.go b/config/confighttp/compression_test.go | |
| index 7f7404b09..1a3f59b88 100644 | |
| --- a/config/confighttp/compression_test.go | |
| +++ b/config/confighttp/compression_test.go | |
| @@ -613,3 +613,184 @@ func compressLz4(tb testing.TB, body []byte) *bytes.Buffer { | |
| require.NoError(tb, lz.Close()) | |
| return &buf | |
| } | |
| + | |
| +// compressRoundTripperNoPool is a version of compressRoundTripper without sync.Pool | |
| +// for benchmarking comparison | |
| +type compressRoundTripperNoPool struct { | |
| + rt http.RoundTripper | |
| + compressionType configcompression.Type | |
| + compressionParams configcompression.CompressionParams | |
| + compressor *compressor | |
| +} | |
| + | |
| +func newCompressRoundTripperNoPool(rt http.RoundTripper, compressionType configcompression.Type, compressionParams configcompression.CompressionParams) (*compressRoundTripperNoPool, error) { | |
| + encoder, err := newCompressor(compressionType, compressionParams) | |
| + if err != nil { | |
| + return nil, err | |
| + } | |
| + return &compressRoundTripperNoPool{ | |
| + rt: rt, | |
| + compressionType: compressionType, | |
| + compressionParams: compressionParams, | |
| + compressor: encoder, | |
| + }, nil | |
| +} | |
| + | |
| +func (r *compressRoundTripperNoPool) RoundTrip(req *http.Request) (*http.Response, error) { | |
| + if req.Header.Get(headerContentEncoding) != "" { | |
| + return r.rt.RoundTrip(req) | |
| + } | |
| + | |
| + // Create new buffer every time (no pooling) | |
| + buf := bytes.NewBuffer([]byte{}) | |
| + if err := r.compressor.compress(buf, req.Body); err != nil { | |
| + return nil, err | |
| + } | |
| + | |
| + cReq, err := http.NewRequestWithContext(req.Context(), req.Method, req.URL.String(), buf) | |
| + if err != nil { | |
| + return nil, err | |
| + } | |
| + | |
| + cReq.Header = req.Header.Clone() | |
| + cReq.Header.Add(headerContentEncoding, string(r.compressionType)) | |
| + | |
| + return r.rt.RoundTrip(cReq) | |
| +} | |
| + | |
| +// BenchmarkBufferAllocation compares buffer allocation performance with and without sync.Pool | |
| +func BenchmarkBufferAllocation(b *testing.B) { | |
| + payload := make([]byte, 10*1024) // 10KB test data | |
| + for i := range payload { | |
| + payload[i] = byte(i % 256) | |
| + } | |
| + | |
| + compressionParams := newCompressionParams(gzip.DefaultCompression) | |
| + | |
| + b.Run("WithPool", func(b *testing.B) { | |
| + rt, err := newCompressRoundTripper(http.DefaultTransport, configcompression.TypeGzip, compressionParams) | |
| + require.NoError(b, err) | |
| + | |
| + b.ResetTimer() | |
| + b.ReportAllocs() | |
| + | |
| + for i := 0; i < b.N; i++ { | |
| + req, err := http.NewRequest("POST", "http://example.com", bytes.NewReader(payload)) | |
| + require.NoError(b, err) | |
| + | |
| + // Only test the buffer allocation and compression part | |
| + buf := rt.bufferPool.Get().(*bytes.Buffer) | |
| + err = rt.compressor.compress(buf, req.Body) | |
| + require.NoError(b, err) | |
| + buf.Reset() | |
| + rt.bufferPool.Put(buf) | |
| + } | |
| + }) | |
| + | |
| + b.Run("NoPool", func(b *testing.B) { | |
| + rt, err := newCompressRoundTripperNoPool(http.DefaultTransport, configcompression.TypeGzip, compressionParams) | |
| + require.NoError(b, err) | |
| + | |
| + b.ResetTimer() | |
| + b.ReportAllocs() | |
| + | |
| + for i := 0; i < b.N; i++ { | |
| + req, err := http.NewRequest("POST", "http://example.com", bytes.NewReader(payload)) | |
| + require.NoError(b, err) | |
| + | |
| + // Test buffer allocation without pool | |
| + buf := bytes.NewBuffer([]byte{}) | |
| + err = rt.compressor.compress(buf, req.Body) | |
| + require.NoError(b, err) | |
| + } | |
| + }) | |
| +} | |
| + | |
| +// BenchmarkCompressRoundTripper compares performance with and without sync.Pool | |
| +func BenchmarkCompressRoundTripper(b *testing.B) { | |
| + testData := []struct { | |
| + name string | |
| + dataSize int | |
| + }{ | |
| + {"Small_1KB", 1024}, | |
| + {"Medium_10KB", 10 * 1024}, | |
| + {"Large_100KB", 100 * 1024}, | |
| + {"XLarge_1MB", 1024 * 1024}, | |
| + } | |
| + | |
| + compressionTypes := []configcompression.Type{ | |
| + configcompression.TypeGzip, | |
| + configcompression.TypeZstd, | |
| + configcompression.TypeSnappy, | |
| + } | |
| + | |
| + for _, data := range testData { | |
| + for _, compressionType := range compressionTypes { | |
| + // Create test payload | |
| + payload := make([]byte, data.dataSize) | |
| + for i := range payload { | |
| + payload[i] = byte(i % 256) | |
| + } | |
| + | |
| + compressionParams := newCompressionParams(gzip.DefaultCompression) | |
| + | |
| + b.Run(fmt.Sprintf("%s_%s_WithPool", data.name, compressionType), func(b *testing.B) { | |
| + // Set up a test server that just returns OK | |
| + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| + w.WriteHeader(http.StatusOK) | |
| + })) | |
| + defer server.Close() | |
| + | |
| + // Create round tripper with pool | |
| + rt, err := newCompressRoundTripper(http.DefaultTransport, compressionType, compressionParams) | |
| + require.NoError(b, err) | |
| + | |
| + client := &http.Client{Transport: rt} | |
| + | |
| + b.ResetTimer() | |
| + b.ReportAllocs() | |
| + | |
| + b.RunParallel(func(pb *testing.PB) { | |
| + for pb.Next() { | |
| + req, err := http.NewRequest("POST", server.URL, bytes.NewReader(payload)) | |
| + require.NoError(b, err) | |
| + req.Header.Set("Content-Type", "application/octet-stream") | |
| + | |
| + resp, err := client.Do(req) | |
| + require.NoError(b, err) | |
| + resp.Body.Close() | |
| + } | |
| + }) | |
| + }) | |
| + | |
| + b.Run(fmt.Sprintf("%s_%s_NoPool", data.name, compressionType), func(b *testing.B) { | |
| + // Set up a test server that just returns OK | |
| + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| + w.WriteHeader(http.StatusOK) | |
| + })) | |
| + defer server.Close() | |
| + | |
| + // Create round tripper without pool | |
| + rt, err := newCompressRoundTripperNoPool(http.DefaultTransport, compressionType, compressionParams) | |
| + require.NoError(b, err) | |
| + | |
| + client := &http.Client{Transport: rt} | |
| + | |
| + b.ResetTimer() | |
| + b.ReportAllocs() | |
| + | |
| + b.RunParallel(func(pb *testing.PB) { | |
| + for pb.Next() { | |
| + req, err := http.NewRequest("POST", server.URL, bytes.NewReader(payload)) | |
| + require.NoError(b, err) | |
| + req.Header.Set("Content-Type", "application/octet-stream") | |
| + | |
| + resp, err := client.Do(req) | |
| + require.NoError(b, err) | |
| + resp.Body.Close() | |
| + } | |
| + }) | |
| + }) | |
| + } | |
| + } | |
| +} | |
| -- | |
| 2.39.5 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment