From: Brad Fitzpatrick Date: Tue, 11 Feb 2014 19:41:25 +0000 (-0800) Subject: archive/zip: re-use flate.Writers when writing compressed files X-Git-Tag: go1.3beta1~769 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=517f4a96837e345609aca6f5bdf1fbeb92c70647;p=gostls13.git archive/zip: re-use flate.Writers when writing compressed files Prevents a ton of garbage. (Noticed this when writing large Camlistore zip archives to Amazon Glacier) Note that the Closer part of the io.WriteCloser is never given to users. It's an internal detail of the package. benchmark old ns/op new ns/op delta BenchmarkCompressedZipGarbage 42884123 40732373 -5.02% benchmark old allocs new allocs delta BenchmarkCompressedZipGarbage 204 149 -26.96% benchmark old bytes new bytes delta BenchmarkCompressedZipGarbage 4397576 66744 -98.48% LGTM=adg, rsc R=adg, rsc CC=golang-codereviews https://golang.org/cl/54300053 --- diff --git a/src/pkg/archive/zip/register.go b/src/pkg/archive/zip/register.go index c046f081b7..4211ec7af7 100644 --- a/src/pkg/archive/zip/register.go +++ b/src/pkg/archive/zip/register.go @@ -6,6 +6,7 @@ package zip import ( "compress/flate" + "errors" "io" "io/ioutil" "sync" @@ -21,12 +22,50 @@ type Compressor func(io.Writer) (io.WriteCloser, error) // when they're finished reading. type Decompressor func(io.Reader) io.ReadCloser +var flateWriterPool sync.Pool + +func newFlateWriter(w io.Writer) io.WriteCloser { + fw, ok := flateWriterPool.Get().(*flate.Writer) + if ok { + fw.Reset(w) + } else { + fw, _ = flate.NewWriter(w, 5) + } + return &pooledFlateWriter{fw: fw} +} + +type pooledFlateWriter struct { + mu sync.Mutex // guards Close and Write + fw *flate.Writer +} + +func (w *pooledFlateWriter) Write(p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + if w.fw == nil { + return 0, errors.New("Write after Close") + } + return w.fw.Write(p) +} + +func (w *pooledFlateWriter) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + var err error + if w.fw != nil { + err = w.fw.Close() + flateWriterPool.Put(w.fw) + w.fw = nil + } + return err +} + var ( mu sync.RWMutex // guards compressor and decompressor maps compressors = map[uint16]Compressor{ Store: func(w io.Writer) (io.WriteCloser, error) { return &nopCloser{w}, nil }, - Deflate: func(w io.Writer) (io.WriteCloser, error) { return flate.NewWriter(w, 5) }, + Deflate: func(w io.Writer) (io.WriteCloser, error) { return newFlateWriter(w), nil }, } decompressors = map[uint16]Decompressor{