aboutsummaryrefslogtreecommitdiffstats
path: root/packfile/reader.go
diff options
context:
space:
mode:
authorToni Cárdenas <toni@tcardenas.me>2015-05-19 19:24:27 +0200
committerToni Cárdenas <toni@tcardenas.me>2015-05-19 19:24:27 +0200
commitc088fd6a7e1a38e9d5a9815265cb575bb08d08ff (patch)
tree3d240aec2afc446a64ef1e165d8b78c2a8b80ac3 /packfile/reader.go
parent5fddbeb678bd2c36c5e5c891ab8f2b143ced5baf (diff)
downloadgo-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/reader.go')
-rw-r--r--packfile/reader.go36
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:])