Skip to content

Commit

Permalink
gzhttp: Use strings for randomJitter to skip a copy (#767)
Browse files Browse the repository at this point in the history
* gzhttp: Use strings for randomJitter to skip a copy

* gzhttp: Use crc32.Update instead of crc32.New

Skips another allocation:

name        old time/op    new time/op    delta
2kJitter-8    29.4µs ± 4%    29.0µs ± 3%  -1.30%  (p=0.006 n=24+24)

name        old speed      new speed      delta
2kJitter-8  69.7MB/s ± 4%  70.6MB/s ± 3%  +1.30%  (p=0.006 n=24+24)

name        old alloc/op   new alloc/op   delta
2kJitter-8    3.40kB ± 4%    3.34kB ± 4%  -1.94%  (p=0.001 n=25+25)

name        old allocs/op  new allocs/op  delta
2kJitter-8      16.0 ± 0%      15.0 ± 0%  -6.25%  (p=0.000 n=25+25)
  • Loading branch information
greatroar committed Mar 10, 2023
1 parent 7501499 commit d900f26
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 50 deletions.
21 changes: 9 additions & 12 deletions gzhttp/compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package gzhttp

import (
"bufio"
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
Expand Down Expand Up @@ -76,7 +75,7 @@ type GzipResponseWriter struct {
suffixETag string // Suffix to add to ETag header if response is compressed.
dropETag bool // Drop ETag header if response is compressed (supersedes suffixETag).
sha256Jitter bool // Use sha256 for jitter.
randomJitter []byte // Add random bytes to output as header field.
randomJitter string // Add random bytes to output as header field.
jitterBuffer int // Maximum buffer to accumulate before doing jitter.

contentTypeFilter func(ct string) bool // Only compress if the response is one of these content-types. All are accepted if empty.
Expand Down Expand Up @@ -216,7 +215,7 @@ func (w *GzipResponseWriter) startGzip(remain []byte) error {
// Initialize the GZIP response.
w.init()

// Set random jitter based on CRC of current buffer
// Set random jitter based on CRC or SHA-256 of current buffer.
// Before first write.
if len(w.randomJitter) > 0 {
var jitRNG uint32
Expand All @@ -232,20 +231,19 @@ func (w *GzipResponseWriter) startGzip(remain []byte) error {
}
h.Write(remain)
}
var tmp [sha256.BlockSize]byte
var tmp [sha256.Size]byte
jitRNG = binary.LittleEndian.Uint32(h.Sum(tmp[:0]))
} else {
h := crc32.New(castagnoliTable)
h.Write(w.buf)
h := crc32.Update(0, castagnoliTable, w.buf)
// Use only up to "w.jitterBuffer", otherwise the output depends on write sizes.
if len(remain) > 0 && len(w.buf) < w.jitterBuffer {
remain := remain
if len(remain)+len(w.buf) > w.jitterBuffer {
remain = remain[:w.jitterBuffer-len(w.buf)]
}
h.Write(remain)
h = crc32.Update(h, castagnoliTable, remain)
}
jitRNG = bits.RotateLeft32(h.Sum32(), 19) ^ 0xab0755de
jitRNG = bits.RotateLeft32(h, 19) ^ 0xab0755de
}
} else {
// Get from rand.Reader
Expand Down Expand Up @@ -526,7 +524,7 @@ type config struct {
suffixETag string
dropETag bool
jitterBuffer int
randomJitter []byte
randomJitter string
sha256Jitter bool
}

Expand Down Expand Up @@ -721,14 +719,13 @@ func RandomJitter(n, buffer int, paranoid bool) option {
return func(c *config) {
if n > 0 {
c.sha256Jitter = paranoid
c.randomJitter = bytes.Repeat([]byte("Padding-"), 1+(n/8))
c.randomJitter = c.randomJitter[:(n + 1)]
c.randomJitter = strings.Repeat("Padding-", 1+(n/8))[:n+1]
c.jitterBuffer = buffer
if c.jitterBuffer == 0 {
c.jitterBuffer = 64 << 10
}
} else {
c.randomJitter = nil
c.randomJitter = ""
c.jitterBuffer = 0
}
}
Expand Down
22 changes: 6 additions & 16 deletions gzhttp/writer/gzkp/gzkp.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,13 @@ func NewWriter(w io.Writer, level int) writer.GzipWriter {
}
}

// SetHeader will override header with any non-nil values.
// SetHeader will override the gzip header on pw.
func (pw *pooledWriter) SetHeader(h writer.Header) {
if h.Name != nil {
pw.Name = string(h.Name)
}
if h.Extra != nil {
pw.Extra = *h.Extra
}
if h.Comment != nil {
pw.Comment = string(h.Comment)
}
if h.ModTime != nil {
pw.ModTime = *h.ModTime
}
if h.OS != nil {
pw.OS = *h.OS
}
pw.Name = h.Name
pw.Extra = h.Extra
pw.Comment = h.Comment
pw.ModTime = h.ModTime
pw.OS = h.OS
}

func Levels() (min, max int) {
Expand Down
22 changes: 6 additions & 16 deletions gzhttp/writer/gzstd/stdlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,13 @@ func NewWriter(w io.Writer, level int) writer.GzipWriter {
}
}

// SetHeader will override header with any non-nil values.
// SetHeader will override the gzip header on pw.
func (pw *pooledWriter) SetHeader(h writer.Header) {
if h.Name != nil {
pw.Name = string(h.Name)
}
if h.Extra != nil {
pw.Extra = *h.Extra
}
if h.Comment != nil {
pw.Comment = string(h.Comment)
}
if h.ModTime != nil {
pw.ModTime = *h.ModTime
}
if h.OS != nil {
pw.OS = *h.OS
}
pw.Name = h.Name
pw.Extra = h.Extra
pw.Comment = h.Comment
pw.ModTime = h.ModTime
pw.OS = h.OS
}

func Levels() (min, max int) {
Expand Down
12 changes: 6 additions & 6 deletions gzhttp/writer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ type GzipWriterExt interface {
SetHeader(h Header)
}

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

// GzipWriterFactory contains the information needed for custom gzip implementations.
Expand Down

0 comments on commit d900f26

Please sign in to comment.