diff options
author | Toni Cárdenas <toni@tcardenas.me> | 2015-05-19 19:24:27 +0200 |
---|---|---|
committer | Toni Cárdenas <toni@tcardenas.me> | 2015-05-19 19:24:27 +0200 |
commit | c088fd6a7e1a38e9d5a9815265cb575bb08d08ff (patch) | |
tree | 3d240aec2afc446a64ef1e165d8b78c2a8b80ac3 /packfile | |
parent | 5fddbeb678bd2c36c5e5c891ab8f2b143ced5baf (diff) | |
download | go-git-c088fd6a7e1a38e9d5a9815265cb575bb08d08ff.tar.gz |
Reduce allocs in packfile reader.
This doesn't have a noticeable impact in performance for a single repo, but it may have for lots of repos due to GC overhead, as we are creating thousands of spurious objects per repo.
More importantly, it was driving me crazy.
Diffstat (limited to 'packfile')
-rw-r--r-- | packfile/reader.go | 36 |
1 files changed, 24 insertions, 12 deletions
diff --git a/packfile/reader.go b/packfile/reader.go index 3725822..4c110b4 100644 --- a/packfile/reader.go +++ b/packfile/reader.go @@ -123,7 +123,13 @@ func (p *PackfileReader) readInt32() (uint32, error) { } func (p *PackfileReader) readObjects(packfile *Packfile) error { + // This code has 50-80 µs of overhead per object not counting zlib inflation. + // Together with zlib inflation, it's 400-410 µs for small objects. + // That's 1 sec for ~2450 objects, ~4.20 MB, or ~250 ms per MB, + // of which 12-20 % is _not_ zlib inflation (ie. is our code). + p.startedGivingBack = true + var unknownForBytes [4]byte offset := 12 for i := 0; i < packfile.ObjectCount; i++ { @@ -135,8 +141,7 @@ func (p *PackfileReader) readObjects(packfile *Packfile) error { p.offsets[offset] = r.hash offset += r.counter + 4 - unknownForBytes := make([]byte, 4) - p.r.Read(unknownForBytes) + p.r.Read(unknownForBytes[:]) if err == io.EOF { break @@ -195,8 +200,8 @@ func (p *PackfileReader) readObject(packfile *Packfile, offset int) (*objectRead func newObjectReader(pr *PackfileReader, pf *Packfile, offset int) (*objectReader, error) { o := &objectReader{pr: pr, pf: pf, offset: offset} - buf := make([]byte, 1) - if _, err := o.Read(buf); err != nil { + var buf [1]byte + if _, err := o.Read(buf[:]); err != nil { return nil, err } @@ -205,7 +210,7 @@ func newObjectReader(pr *PackfileReader, pf *Packfile, offset int) (*objectReade var shift uint = 4 for buf[0]&0x80 == 0x80 { - if _, err := o.Read(buf); err != nil { + if _, err := o.Read(buf[:]); err != nil { return nil, err } @@ -217,8 +222,8 @@ func newObjectReader(pr *PackfileReader, pf *Packfile, offset int) (*objectReade } func (o *objectReader) readREFDelta() error { - ref := make([]byte, 20) - o.Read(ref) + var ref [20]byte + o.Read(ref[:]) buf, err := o.inflate() if err != nil { @@ -228,9 +233,9 @@ func (o *objectReader) readREFDelta() error { refhash := fmt.Sprintf("%x", ref) referenced, ok := o.pr.objects[refhash] if !ok { - o.pr.deltas = append(o.pr.deltas, packfileDelta{hash: refhash, delta: buf}) + o.pr.deltas = append(o.pr.deltas, packfileDelta{hash: refhash, delta: buf[:]}) } else { - patched := PatchDelta(referenced.bytes, buf) + patched := PatchDelta(referenced.bytes, buf[:]) if patched == nil { return NewError("error while patching %x", ref) } @@ -329,8 +334,8 @@ func (o *objectReader) addObject(bytes []byte) error { func (o *objectReader) inflate() ([]byte, error) { //Quick fix "Invalid git object tag '\x00'" when the length of a object is 0 if o.size == 0 { - buf := make([]byte, 4) - if _, err := o.Read(buf); err != nil { + var buf [4]byte + if _, err := o.Read(buf[:]); err != nil { return nil, err } @@ -352,7 +357,14 @@ func (o *objectReader) inflate() ([]byte, error) { return nil, NewError("the object size exceeed the allowed limit: %d", o.size) } - buf := make([]byte, o.size) + var arrbuf [4096]byte // Stack-allocated for <4 KB objects. + var buf []byte + if uint64(len(arrbuf)) >= o.size { + buf = arrbuf[:o.size] + } else { + buf = make([]byte, o.size) + } + read := 0 for read < int(o.size) { n, err := zr.Read(buf[read:]) |