Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gzhttp: Remove a few unneeded allocs #768

Merged
merged 1 commit into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions gzhttp/compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ func (w *GzipResponseWriter) startGzip(remain []byte) error {
}
h.Write(remain)
}
jitRNG = binary.LittleEndian.Uint32(h.Sum(nil))
var tmp [sha256.BlockSize]byte
jitRNG = binary.LittleEndian.Uint32(h.Sum(tmp[:0]))
} else {
// Get from rand.Reader
var tmp [4]byte
Expand All @@ -236,9 +237,8 @@ func (w *GzipResponseWriter) startGzip(remain []byte) error {
}
jitRNG = binary.LittleEndian.Uint32(tmp[:])
}
jit := string(w.randomJitter[:1+jitRNG%uint32(len(w.randomJitter)-1)])
//fmt.Println("w.buf:", len(w.buf), "remain:", len(remain), "jitter:", len(jit))
w.gw.(writer.GzipWriterExt).SetHeader(writer.Header{Comment: &jit})
jit := w.randomJitter[:1+jitRNG%uint32(len(w.randomJitter)-1)]
w.gw.(writer.GzipWriterExt).SetHeader(writer.Header{Comment: jit})
}
n, err := w.gw.Write(w.buf)

Expand Down Expand Up @@ -457,6 +457,7 @@ func NewWrapper(opts ...option) (func(http.Handler) http.HandlerFunc, error) {
} else {
h.ServeHTTP(gw, r)
}
w.Header().Del(HeaderNoCompression)
} else {
h.ServeHTTP(newNoGzipResponseWriter(w), r)
w.Header().Del(HeaderNoCompression)
Expand Down Expand Up @@ -786,10 +787,23 @@ func parseEncodings(s string) (codings, error) {
return c, nil
}

var errEmptyEncoding = errors.New("empty content-coding")

// parseCoding parses a single coding (content-coding with an optional qvalue),
// as might appear in an Accept-Encoding header. It attempts to forgive minor
// formatting errors.
func parseCoding(s string) (coding string, qvalue float64, err error) {
// Avoid splitting if we can...
if len(s) == 0 {
return "", 0, errEmptyEncoding
}
if !strings.ContainsRune(s, ';') {
coding = strings.ToLower(strings.TrimSpace(s))
if coding == "" {
err = errEmptyEncoding
}
return coding, DefaultQValue, err
}
for n, part := range strings.Split(s, ";") {
part = strings.TrimSpace(part)
qvalue = DefaultQValue
Expand All @@ -808,7 +822,7 @@ func parseCoding(s string) (coding string, qvalue float64, err error) {
}

if coding == "" {
err = fmt.Errorf("empty content-coding")
err = errEmptyEncoding
}

return
Expand Down Expand Up @@ -850,6 +864,9 @@ const intSize = 32 << (^uint(0) >> 63)

// atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
func atoi(s string) (int, bool) {
if len(s) == 0 {
return 0, false
}
sLen := len(s)
if intSize == 32 && (0 < sLen && sLen < 10) ||
intSize == 64 && (0 < sLen && sLen < 19) {
Expand Down
65 changes: 48 additions & 17 deletions gzhttp/compress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1438,19 +1438,50 @@ func TestContentTypeDetect(t *testing.T) {

// --------------------------------------------------------------------

func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048, gzip.DefaultCompression) }
func BenchmarkGzipHandler_S20k(b *testing.B) { benchmark(b, false, 20480, gzip.DefaultCompression) }
func BenchmarkGzipHandler_S100k(b *testing.B) { benchmark(b, false, 102400, gzip.DefaultCompression) }
func BenchmarkGzipHandler_P2k(b *testing.B) { benchmark(b, true, 2048, gzip.DefaultCompression) }
func BenchmarkGzipHandler_P20k(b *testing.B) { benchmark(b, true, 20480, gzip.DefaultCompression) }
func BenchmarkGzipHandler_P100k(b *testing.B) { benchmark(b, true, 102400, gzip.DefaultCompression) }

func BenchmarkGzipBestSpeedHandler_S2k(b *testing.B) { benchmark(b, false, 2048, gzip.BestSpeed) }
func BenchmarkGzipBestSpeedHandler_S20k(b *testing.B) { benchmark(b, false, 20480, gzip.BestSpeed) }
func BenchmarkGzipBestSpeedHandler_S100k(b *testing.B) { benchmark(b, false, 102400, gzip.BestSpeed) }
func BenchmarkGzipBestSpeedHandler_P2k(b *testing.B) { benchmark(b, true, 2048, gzip.BestSpeed) }
func BenchmarkGzipBestSpeedHandler_P20k(b *testing.B) { benchmark(b, true, 20480, gzip.BestSpeed) }
func BenchmarkGzipBestSpeedHandler_P100k(b *testing.B) { benchmark(b, true, 102400, gzip.BestSpeed) }
func BenchmarkGzipHandler_S2k(b *testing.B) {
benchmark(b, false, 2048, CompressionLevel(gzip.DefaultCompression))
}
func BenchmarkGzipHandler_S20k(b *testing.B) {
benchmark(b, false, 20480, CompressionLevel(gzip.DefaultCompression))
}
func BenchmarkGzipHandler_S100k(b *testing.B) {
benchmark(b, false, 102400, CompressionLevel(gzip.DefaultCompression))
}
func BenchmarkGzipHandler_P2k(b *testing.B) {
benchmark(b, true, 2048, CompressionLevel(gzip.DefaultCompression))
}
func BenchmarkGzipHandler_P20k(b *testing.B) {
benchmark(b, true, 20480, CompressionLevel(gzip.DefaultCompression))
}
func BenchmarkGzipHandler_P100k(b *testing.B) {
benchmark(b, true, 102400, CompressionLevel(gzip.DefaultCompression))
}

func BenchmarkGzipBestSpeedHandler_S2k(b *testing.B) {
benchmark(b, false, 2048, CompressionLevel(gzip.BestSpeed))
}
func BenchmarkGzipBestSpeedHandler_S20k(b *testing.B) {
benchmark(b, false, 20480, CompressionLevel(gzip.BestSpeed))
}
func BenchmarkGzipBestSpeedHandler_S100k(b *testing.B) {
benchmark(b, false, 102400, CompressionLevel(gzip.BestSpeed))
}
func BenchmarkGzipBestSpeedHandler_P2k(b *testing.B) {
benchmark(b, true, 2048, CompressionLevel(gzip.BestSpeed))
}
func BenchmarkGzipBestSpeedHandler_P20k(b *testing.B) {
benchmark(b, true, 20480, CompressionLevel(gzip.BestSpeed))
}
func BenchmarkGzipBestSpeedHandler_P100k(b *testing.B) {
benchmark(b, true, 102400, CompressionLevel(gzip.BestSpeed))
}

func Benchmark2kJitter(b *testing.B) {
benchmark(b, false, 2048, CompressionLevel(gzip.BestSpeed), RandomJitter(32, 0))
}
func Benchmark2kJitterRNG(b *testing.B) {
benchmark(b, false, 2048, CompressionLevel(gzip.BestSpeed), RandomJitter(32, -1))
}

// --------------------------------------------------------------------

Expand All @@ -1462,15 +1493,15 @@ func gzipStrLevel(s []byte, lvl int) []byte {
return b.Bytes()
}

func benchmark(b *testing.B, parallel bool, size, level int) {
func benchmark(b *testing.B, parallel bool, size int, opts ...option) {
bin, err := os.ReadFile("testdata/benchmark.json")
if err != nil {
b.Fatal(err)
}

req, _ := http.NewRequest("GET", "/whatever", nil)
req.Header.Set("Accept-Encoding", "gzip")
handler := newTestHandlerLevel(bin[:size], level)
handler := newTestHandlerLevel(bin[:size], opts...)

b.ReportAllocs()
b.SetBytes(int64(size))
Expand Down Expand Up @@ -1514,8 +1545,8 @@ func newTestHandler(body []byte) http.Handler {
}))
}

func newTestHandlerLevel(body []byte, level int) http.Handler {
wrapper, err := NewWrapper(CompressionLevel(level))
func newTestHandlerLevel(body []byte, opts ...option) http.Handler {
wrapper, err := NewWrapper(opts...)
if err != nil {
panic(err)
}
Expand Down
4 changes: 2 additions & 2 deletions gzhttp/writer/gzkp/gzkp.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ func NewWriter(w io.Writer, level int) writer.GzipWriter {
// SetHeader will override header with any non-nil values.
func (pw *pooledWriter) SetHeader(h writer.Header) {
if h.Name != nil {
pw.Name = *h.Name
pw.Name = string(h.Name)
}
if h.Extra != nil {
pw.Extra = *h.Extra
}
if h.Comment != nil {
pw.Comment = *h.Comment
pw.Comment = string(h.Comment)
}
if h.ModTime != nil {
pw.ModTime = *h.ModTime
Expand Down
4 changes: 2 additions & 2 deletions gzhttp/writer/gzstd/stdlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ func NewWriter(w io.Writer, level int) writer.GzipWriter {
// SetHeader will override header with any non-nil values.
func (pw *pooledWriter) SetHeader(h writer.Header) {
if h.Name != nil {
pw.Name = *h.Name
pw.Name = string(h.Name)
}
if h.Extra != nil {
pw.Extra = *h.Extra
}
if h.Comment != nil {
pw.Comment = *h.Comment
pw.Comment = string(h.Comment)
}
if h.ModTime != nil {
pw.ModTime = *h.ModTime
Expand Down
4 changes: 2 additions & 2 deletions gzhttp/writer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ type GzipWriterExt interface {

// Header provides nillable header fields.
type Header struct {
Comment *string // comment
Comment []byte // comment, converted to string if set.
Extra *[]byte // "extra data"
ModTime *time.Time // modification time
Name *string // file name
Name []byte // file name, converted to string if set.
OS *byte // operating system type
}

Expand Down