Skip to content

Commit dbdb42b

Browse files
authored
feat: shrink allocations when setting new elements (#90)
`bytes.Buffer` will often over-allocate. Here, we copy to an exact-sized buffer to avoid this. We'll pay some extra copying cost, but we can at least avoid the allocation overhead with a buffer pool in most cases.
1 parent f6a28b2 commit dbdb42b

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

amt.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package amt
22

33
import (
4-
"bytes"
54
"context"
65
"fmt"
76
"math"
@@ -149,11 +148,11 @@ func (r *Root) Set(ctx context.Context, i uint64, val cbg.CBORMarshaler) error {
149148
if val == nil {
150149
d.Raw = cbg.CborNull
151150
} else {
152-
valueBuf := new(bytes.Buffer)
153-
if err := val.MarshalCBOR(valueBuf); err != nil {
151+
data, err := cborToBytes(val)
152+
if err != nil {
154153
return err
155154
}
156-
d.Raw = valueBuf.Bytes()
155+
d.Raw = data
157156
}
158157

159158
// where the index is greater than the number of elements we can fit into the

util.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package amt
22

3-
import "math"
3+
import (
4+
"bytes"
5+
"math"
6+
"sync"
7+
8+
cbg "github.com/whyrusleeping/cbor-gen"
9+
)
410

511
// Given height 'height', how many nodes in a maximally full tree can we
612
// build? (bitWidth^2)^height = width^height. If we pass in height+1 we can work
@@ -13,3 +19,29 @@ func nodesForHeight(bitWidth uint, height int) uint64 {
1319
}
1420
return 1 << heightLogTwo
1521
}
22+
23+
var bufferPool = sync.Pool{
24+
New: func() any {
25+
return bytes.NewBuffer(nil)
26+
},
27+
}
28+
29+
func cborToBytes(val cbg.CBORMarshaler) ([]byte, error) {
30+
// Temporary location to put values. We'll copy them to an exact-sized buffer when done.
31+
valueBuf := bufferPool.Get().(*bytes.Buffer)
32+
defer func() {
33+
valueBuf.Reset()
34+
bufferPool.Put(valueBuf)
35+
}()
36+
37+
if err := val.MarshalCBOR(valueBuf); err != nil {
38+
return nil, err
39+
}
40+
41+
// Copy to shrink the allocation.
42+
buf := valueBuf.Bytes()
43+
cpy := make([]byte, len(buf))
44+
copy(cpy, buf)
45+
46+
return cpy, nil
47+
}

0 commit comments

Comments
 (0)