Skip to content

Commit

Permalink
benchmark all available compression levels
Browse files Browse the repository at this point in the history
  • Loading branch information
CAFxX committed Jan 28, 2021
1 parent cba2941 commit 339500f
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 31 deletions.
6 changes: 3 additions & 3 deletions adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"sync"

"github.com/CAFxX/httpcompression/contrib/andybalholm/brotli"
_brotli "github.com/andybalholm/brotli"
)

const (
Expand All @@ -27,6 +26,7 @@ const (
// 200 is a somewhat arbitrary number; in experiments compressing short text/markup-like sequences
// with different compressors we saw that sequences shorter that ~180 the output generated by the
// compressor would sometime be larger than the input.
// This default may change between versions.
// In general there can be no one-size-fits-all value: you will want to measure if a different
// minimum size improves end-to-end performance for your workloads.
DefaultMinSize = 200
Expand Down Expand Up @@ -119,7 +119,7 @@ func addVaryHeader(h http.Header, value string) {
func DefaultAdapter(opts ...Option) (func(http.Handler) http.Handler, error) {
defaults := []Option{
GzipCompressionLevel(gzip.DefaultCompression),
BrotliCompressionLevel(_brotli.DefaultCompression),
BrotliCompressionLevel(brotli.DefaultCompression),
MinSize(DefaultMinSize),
}
opts = append(defaults, opts...)
Expand Down Expand Up @@ -173,7 +173,7 @@ func GzipCompressionLevel(level int) Option {
// The default is 3 (the same default used in the reference brotli C
// implementation).
func BrotliCompressionLevel(level int) Option {
c, err := brotli.New(_brotli.WriterOptions{Quality: level})
c, err := brotli.New(brotli.Options{Quality: level})
if err != nil {
return errorOption(err)
}
Expand Down
63 changes: 42 additions & 21 deletions adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import (
"strconv"
"testing"

"github.com/CAFxX/httpcompression/contrib/andybalholm/brotli"
"github.com/CAFxX/httpcompression/contrib/klauspost/zstd"
"github.com/andybalholm/brotli"
"github.com/stretchr/testify/assert"

ibrotli "github.com/andybalholm/brotli"
kpzstd "github.com/klauspost/compress/zstd"
)

const (
Expand Down Expand Up @@ -908,15 +911,20 @@ func (noopHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {}
func BenchmarkAdapter(b *testing.B) {
for _, size := range []int{10, 100, 1000, 10000, 100000} {
b.Run(fmt.Sprintf("%d", size), func(b *testing.B) {
for _, ae := range []string{"gzip", "br", "zstd"} {
b.Run(ae, func(b *testing.B) {
b.Run("serial", func(b *testing.B) {
benchmark(b, false, size, ae)
})
b.Run("parallel", func(b *testing.B) {
benchmark(b, true, size, ae)
for ae, maxq := range map[string]int{"gzip": 9, "br": 11, "zstd": 4} {
if size < DefaultMinSize {
maxq = 1
}
for q := 1; q <= maxq; q++ {
b.Run(fmt.Sprintf("%s/%d", ae, q), func(b *testing.B) {
b.Run("serial", func(b *testing.B) {
benchmark(b, false, size, ae, q)
})
b.Run("parallel", func(b *testing.B) {
benchmark(b, true, size, ae, q)
})
})
})
}
}
})
}
Expand All @@ -934,36 +942,47 @@ func gzipStrLevel(s string, lvl int) []byte {

func brotliStrLevel(s string, lvl int) []byte {
var b bytes.Buffer
w := brotli.NewWriterLevel(&b, lvl)
w := ibrotli.NewWriterLevel(&b, lvl)
io.WriteString(w, s)
w.Close()
return b.Bytes()
}

func benchmark(b *testing.B, parallel bool, size int, ae string) {
func benchmark(b *testing.B, parallel bool, size int, ae string, d int) {
bin, err := ioutil.ReadFile("testdata/benchmark.json")
if err != nil {
b.Fatal(err)
}

zenc, _ := zstd.New()
var enc CompressorProvider
switch ae {
case "gzip":
enc, err = NewDefaultGzipCompressor(d)
case "br":
enc, err = brotli.New(brotli.Options{Quality: d})
case "zstd":
enc, err = zstd.New(kpzstd.WithEncoderLevel(kpzstd.EncoderLevel(d)))
}
if err != nil {
b.Fatal(err)
}

req, _ := http.NewRequest("GET", "/whatever", nil)
req.Header.Set("Accept-Encoding", ae)
handler := newTestHandler(
string(bin[:size]),
Compressor(zstd.Encoding, 2, zenc),
Compressor(ae, 100, enc),
)

res := httptest.NewRecorder()
handler.ServeHTTP(res, req)
if size < 20 {
if size < DefaultMinSize {
if res.Code != 200 || res.Header().Get("Content-Encoding") != "" || res.Body.Len() != size {
b.Fatal(res)
b.Fatalf("code=%d, accept-encoding=%q, body=%d", res.Code, res.Header().Get("Content-Encoding"), res.Body.Len())
}
} else {
if res.Code != 200 || res.Header().Get("Content-Encoding") != ae || res.Body.Len() < size/10 {
b.Fatal(res)
if res.Code != 200 || res.Header().Get("Content-Encoding") != ae || res.Body.Len() < size/10 || res.Body.Len() == size {
b.Fatalf("code=%d, accept-encoding=%q, body=%d", res.Code, res.Header().Get("Content-Encoding"), res.Body.Len())
}
}

Expand All @@ -974,19 +993,22 @@ func benchmark(b *testing.B, parallel bool, size int, ae string) {
for pb.Next() {
res.reset()
handler.ServeHTTP(res, req)
b.ReportMetric(float64(res.b*100)/float64(size), "%")
}
})
} else {
res := &discardResponseWriter{}
for i := 0; i < b.N; i++ {
res.reset()
handler.ServeHTTP(res, req)
b.ReportMetric(float64(res.b*100)/float64(size), "%")
}
}
}

type discardResponseWriter struct {
h http.Header
b int
}

func (w *discardResponseWriter) Header() http.Header {
Expand All @@ -996,17 +1018,16 @@ func (w *discardResponseWriter) Header() http.Header {
return w.h
}

func (*discardResponseWriter) Write(b []byte) (int, error) {
func (w *discardResponseWriter) Write(b []byte) (int, error) {
w.b += len(b)
return len(b), nil
}

func (*discardResponseWriter) WriteHeader(int) {
}

func (w *discardResponseWriter) reset() {
if w.h == nil {
return
}
w.b = 0
for k := range w.h {
delete(w.h, k)
}
Expand Down
4 changes: 2 additions & 2 deletions contrib/andybalholm/brotli/brotli.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
)

const (
Encoding = "br"
DefaultLevel = brotli.DefaultCompression
Encoding = "br"
DefaultCompression = brotli.DefaultCompression
)

type Options = brotli.WriterOptions
Expand Down
4 changes: 2 additions & 2 deletions contrib/klauspost/gzip/gzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

const (
Encoding = "gzip"
DefaultLevel = gzip.DefaultCompression
Encoding = "gzip"
DefaultCompression = gzip.DefaultCompression
)

type compressor struct {
Expand Down
4 changes: 2 additions & 2 deletions contrib/klauspost/pgzip/pgzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

const (
Encoding = "gzip"
DefaultLevel = pgzip.DefaultCompression
Encoding = "gzip"
DefaultCompression = pgzip.DefaultCompression
)

type compressor struct {
Expand Down
5 changes: 4 additions & 1 deletion contrib/klauspost/zstd/zstd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import (
"github.com/klauspost/compress/zstd"
)

const Encoding = "zstd"
const (
Encoding = "zstd"
DefaultCompression = zstd.SpeedDefault
)

type compressor struct {
pool sync.Pool
Expand Down

0 comments on commit 339500f

Please sign in to comment.