rlp: avoid list header allocation in encoder (#21274)

List headers made up 11% of all allocations during sync. This change
removes most of those allocations by keeping the list header values
cached in the encoder buffer instead. Since encoder buffers are pooled,
list headers are no longer allocated in the common case where an
encoder buffer is available for reuse.

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
Marius van der Wijden 2020-07-01 13:49:19 +02:00 committed by GitHub
parent ec51cbb5fb
commit 8dfd66f701
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -93,7 +93,7 @@ func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
type encbuf struct { type encbuf struct {
str []byte // string data, contains everything except list headers str []byte // string data, contains everything except list headers
lheads []*listhead // all list headers lheads []listhead // all list headers
lhsize int // sum of sizes of all encoded list headers lhsize int // sum of sizes of all encoded list headers
sizebuf []byte // 9-byte auxiliary buffer for uint encoding sizebuf []byte // 9-byte auxiliary buffer for uint encoding
} }
@ -137,13 +137,9 @@ var encbufPool = sync.Pool{
func (w *encbuf) reset() { func (w *encbuf) reset() {
w.lhsize = 0 w.lhsize = 0
if w.str != nil {
w.str = w.str[:0] w.str = w.str[:0]
}
if w.lheads != nil {
w.lheads = w.lheads[:0] w.lheads = w.lheads[:0]
} }
}
// encbuf implements io.Writer so it can be passed it into EncodeRLP. // encbuf implements io.Writer so it can be passed it into EncodeRLP.
func (w *encbuf) Write(b []byte) (int, error) { func (w *encbuf) Write(b []byte) (int, error) {
@ -181,13 +177,16 @@ func (w *encbuf) encodeString(b []byte) {
} }
} }
func (w *encbuf) list() *listhead { // list adds a new list header to the header stack. It returns the index
lh := &listhead{offset: len(w.str), size: w.lhsize} // of the header. The caller must call listEnd with this index after encoding
w.lheads = append(w.lheads, lh) // the content of the list.
return lh func (w *encbuf) list() int {
w.lheads = append(w.lheads, listhead{offset: len(w.str), size: w.lhsize})
return len(w.lheads) - 1
} }
func (w *encbuf) listEnd(lh *listhead) { func (w *encbuf) listEnd(index int) {
lh := &w.lheads[index]
lh.size = w.size() - lh.offset - lh.size lh.size = w.size() - lh.offset - lh.size
if lh.size < 56 { if lh.size < 56 {
w.lhsize++ // length encoded into kind tag w.lhsize++ // length encoded into kind tag