]> Cypherpunks repositories - gostls13.git/commitdiff
archive/zip: re-use flate.Writers when writing compressed files
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 11 Feb 2014 19:41:25 +0000 (11:41 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 11 Feb 2014 19:41:25 +0000 (11:41 -0800)
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

src/pkg/archive/zip/register.go

index c046f081b736cd4cb884029097a1308595459d6b..4211ec7af7ba8ee56c5dcd194959230c5a000e07 100644 (file)
@@ -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{