From 9a0c85218c10f9916ed623f4de2302e61c3de995 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 2 Apr 2024 09:38:12 -0400 Subject: [PATCH] Remove memory alloc from encodeDBNode (#2893) --- x/merkledb/codec.go | 15 +- x/merkledb/codec_test.go | 510 +++++++++++++++++++++++++++++---------- 2 files changed, 389 insertions(+), 136 deletions(-) diff --git a/x/merkledb/codec.go b/x/merkledb/codec.go index a820c42df7cc..70fb8f3788ac 100644 --- a/x/merkledb/codec.go +++ b/x/merkledb/codec.go @@ -13,8 +13,6 @@ import ( "math/bits" "slices" - "golang.org/x/exp/maps" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/maybe" ) @@ -139,9 +137,16 @@ func encodeDBNode(n *dbNode) []byte { buf := bytes.NewBuffer(make([]byte, 0, encodedDBNodeSize(n))) encodeMaybeByteSlice(buf, n.value) encodeUint(buf, uint64(len(n.children))) - // Note we insert children in order of increasing index - // for determinism. - keys := maps.Keys(n.children) + + // By allocating BranchFactorLargest rather than len(n.children), this slice + // is allocated on the stack rather than the heap. BranchFactorLargest is + // at least len(n.children) which avoids memory allocations. + keys := make([]byte, 0, BranchFactorLargest) + for k := range n.children { + keys = append(keys, k) + } + + // Ensure that the order of entries is correct. slices.Sort(keys) for _, index := range keys { entry := n.children[index] diff --git a/x/merkledb/codec_test.go b/x/merkledb/codec_test.go index 171909d51c58..19aa4d55ed4f 100644 --- a/x/merkledb/codec_test.go +++ b/x/merkledb/codec_test.go @@ -18,6 +18,364 @@ import ( "github.com/ava-labs/avalanchego/utils/maybe" ) +var ( + hashNodeTests = []struct { + name string + n *node + expectedHash string + }{ + { + name: "empty node", + n: newNode(Key{}), + expectedHash: "rbhtxoQ1DqWHvb6w66BZdVyjmPAneZUSwQq9uKj594qvFSdav", + }, + { + name: "has value", + n: func() *node { + n := newNode(Key{}) + n.setValue(maybe.Some([]byte("value1"))) + return n + }(), + expectedHash: "2vx2xueNdWoH2uB4e8hbMU5jirtZkZ1c3ePCWDhXYaFRHpCbnQ", + }, + { + name: "has key", + n: newNode(ToKey([]byte{0, 1, 2, 3, 4, 5, 6, 7})), + expectedHash: "2vA8ggXajhFEcgiF8zHTXgo8T2ALBFgffp1xfn48JEni1Uj5uK", + }, + { + name: "1 child", + n: func() *node { + n := newNode(Key{}) + childNode := newNode(ToKey([]byte{255})) + childNode.setValue(maybe.Some([]byte("value1"))) + n.addChildWithID(childNode, 4, hashNode(childNode)) + return n + }(), + expectedHash: "YfJRufqUKBv9ez6xZx6ogpnfDnw9fDsyebhYDaoaH57D3vRu3", + }, + { + name: "2 children", + n: func() *node { + n := newNode(Key{}) + + childNode1 := newNode(ToKey([]byte{255})) + childNode1.setValue(maybe.Some([]byte("value1"))) + + childNode2 := newNode(ToKey([]byte{237})) + childNode2.setValue(maybe.Some([]byte("value2"))) + + n.addChildWithID(childNode1, 4, hashNode(childNode1)) + n.addChildWithID(childNode2, 4, hashNode(childNode2)) + return n + }(), + expectedHash: "YVmbx5MZtSKuYhzvHnCqGrswQcxmozAkv7xE1vTA2EiGpWUkv", + }, + { + name: "16 children", + n: func() *node { + n := newNode(Key{}) + + for i := byte(0); i < 16; i++ { + childNode := newNode(ToKey([]byte{i << 4})) + childNode.setValue(maybe.Some([]byte("some value"))) + + n.addChildWithID(childNode, 4, hashNode(childNode)) + } + return n + }(), + expectedHash: "5YiFLL7QV3f441See9uWePi3wVKsx9fgvX5VPhU8PRxtLqhwY", + }, + } + encodeDBNodeTests = []struct { + name string + n *dbNode + expectedBytes []byte + }{ + { + name: "empty node", + n: &dbNode{}, + expectedBytes: []byte{ + 0x00, // value.HasValue() + 0x00, // len(children) + }, + }, + { + name: "has value", + n: &dbNode{ + value: maybe.Some([]byte("value")), + }, + expectedBytes: []byte{ + 0x01, // value.HasValue() + 0x05, // len(value.Value()) + 'v', 'a', 'l', 'u', 'e', // value.Value() + 0x00, // len(children) + }, + }, + { + name: "1 child", + n: &dbNode{ + value: maybe.Some([]byte("value")), + children: map[byte]*child{ + 0: { + compressedKey: ToKey([]byte{0}), + id: ids.ID{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }, + hasValue: true, + }, + }, + }, + expectedBytes: []byte{ + 0x01, // value.HasValue() + 0x05, // len(value.Value()) + 'v', 'a', 'l', 'u', 'e', // value.Value() + 0x01, // len(children) + 0x00, // children[0].index + 0x08, // len(children[0].compressedKey) + 0x00, // children[0].compressedKey + // children[0].id + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x01, // children[0].hasValue + }, + }, + { + name: "2 children", + n: &dbNode{ + value: maybe.Some([]byte("value")), + children: map[byte]*child{ + 0: { + compressedKey: ToKey([]byte{0}), + id: ids.ID{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }, + hasValue: true, + }, + 1: { + compressedKey: ToKey([]byte{1, 2, 3}), + id: ids.ID{ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + hasValue: false, + }, + }, + }, + expectedBytes: []byte{ + 0x01, // value.HasValue() + 0x05, // len(value.Value()) + 'v', 'a', 'l', 'u', 'e', // value.Value() + 0x02, // len(children) + 0x00, // children[0].index + 0x08, // len(children[0].compressedKey) + 0x00, // children[0].compressedKey + // children[0].id + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x01, // children[0].hasValue + 0x01, // children[1].index + 0x18, // len(children[1].compressedKey) + 0x01, 0x02, 0x03, // children[1].compressedKey + // children[1].id + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x00, // children[1].hasValue + }, + }, + { + name: "16 children", + n: func() *dbNode { + n := &dbNode{ + value: maybe.Some([]byte("value")), + children: make(map[byte]*child), + } + for i := byte(0); i < 16; i++ { + n.children[i] = &child{ + compressedKey: ToKey([]byte{i}), + id: ids.ID{ + 0x00 + i, 0x01 + i, 0x02 + i, 0x03 + i, + 0x04 + i, 0x05 + i, 0x06 + i, 0x07 + i, + 0x08 + i, 0x09 + i, 0x0a + i, 0x0b + i, + 0x0c + i, 0x0d + i, 0x0e + i, 0x0f + i, + 0x10 + i, 0x11 + i, 0x12 + i, 0x13 + i, + 0x14 + i, 0x15 + i, 0x16 + i, 0x17 + i, + 0x18 + i, 0x19 + i, 0x1a + i, 0x1b + i, + 0x1c + i, 0x1d + i, 0x1e + i, 0x1f + i, + }, + hasValue: i%2 == 0, + } + } + return n + }(), + expectedBytes: []byte{ + 0x01, // value.HasValue() + 0x05, // len(value.Value()) + 'v', 'a', 'l', 'u', 'e', // value.Value() + 0x10, // len(children) + 0x00, // children[0].index + 0x08, // len(children[0].compressedKey) + 0x00, // children[0].compressedKey + // children[0].id + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x01, // children[0].hasValue + 0x01, // children[1].index + 0x08, // len(children[1].compressedKey) + 0x01, // children[1].compressedKey + // children[1].id + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x00, // children[1].hasValue + 0x02, // children[2].index + 0x08, // len(children[2].compressedKey) + 0x02, // children[2].compressedKey + // children[2].id + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, + 0x01, // children[2].hasValue + 0x03, // children[3].index + 0x08, // len(children[3].compressedKey) + 0x03, // children[3].compressedKey + // children[3].id + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x00, // children[3].hasValue + 0x04, // children[4].index + 0x08, // len(children[4].compressedKey) + 0x04, // children[4].compressedKey + // children[4].id + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x01, // children[4].hasValue + 0x05, // children[5].index + 0x08, // len(children[5].compressedKey) + 0x05, // children[5].compressedKey + // children[5].id + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x00, // children[5].hasValue + 0x06, // children[6].index + 0x08, // len(children[6].compressedKey) + 0x06, // children[6].compressedKey + // children[6].id + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x01, // children[6].hasValue + 0x07, // children[7].index + 0x08, // len(children[7].compressedKey) + 0x07, // children[7].compressedKey + // children[7].id + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x00, // children[7].hasValue + 0x08, // children[8].index + 0x08, // len(children[8].compressedKey) + 0x08, // children[8].compressedKey + // children[8].id + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x01, // children[8].hasValue + 0x09, // children[9].index + 0x08, // len(children[9].compressedKey) + 0x09, // children[9].compressedKey + // children[9].id + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x00, // children[9].hasValue + 0x0a, // children[10].index + 0x08, // len(children[10].compressedKey) + 0x0a, // children[10].compressedKey + // children[10].id + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x01, // children[10].hasValue + 0x0b, // children[11].index + 0x08, // len(children[11].compressedKey) + 0x0b, // children[11].compressedKey + // children[11].id + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x00, // children[11].hasValue + 0x0c, // children[12].index + 0x08, // len(children[12].compressedKey) + 0x0c, // children[12].compressedKey + // children[12].id + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x01, // children[12].hasValue + 0x0d, // children[13].index + 0x08, // len(children[13].compressedKey) + 0x0d, // children[13].compressedKey + // children[13].id + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x00, // children[13].hasValue + 0x0e, // children[14].index + 0x08, // len(children[14].compressedKey) + 0x0e, // children[14].compressedKey + // children[14].id + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + 0x01, // children[14].hasValue + 0x0f, // children[15].index + 0x08, // len(children[15].compressedKey) + 0x0f, // children[15].compressedKey + // children[15].id + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x00, // children[15].hasValue + }, + }, + } +) + func FuzzCodecBool(f *testing.F) { f.Fuzz( func( @@ -234,75 +592,7 @@ func FuzzHashNode(f *testing.F) { } func TestHashNode(t *testing.T) { - tests := []struct { - name string - n *node - expectedHash string - }{ - { - name: "empty node", - n: newNode(Key{}), - expectedHash: "rbhtxoQ1DqWHvb6w66BZdVyjmPAneZUSwQq9uKj594qvFSdav", - }, - { - name: "has value", - n: func() *node { - n := newNode(Key{}) - n.setValue(maybe.Some([]byte("value1"))) - return n - }(), - expectedHash: "2vx2xueNdWoH2uB4e8hbMU5jirtZkZ1c3ePCWDhXYaFRHpCbnQ", - }, - { - name: "has key", - n: newNode(ToKey([]byte{0, 1, 2, 3, 4, 5, 6, 7})), - expectedHash: "2vA8ggXajhFEcgiF8zHTXgo8T2ALBFgffp1xfn48JEni1Uj5uK", - }, - { - name: "1 child", - n: func() *node { - n := newNode(Key{}) - childNode := newNode(ToKey([]byte{255})) - childNode.setValue(maybe.Some([]byte("value1"))) - n.addChildWithID(childNode, 4, hashNode(childNode)) - return n - }(), - expectedHash: "YfJRufqUKBv9ez6xZx6ogpnfDnw9fDsyebhYDaoaH57D3vRu3", - }, - { - name: "2 children", - n: func() *node { - n := newNode(Key{}) - - childNode1 := newNode(ToKey([]byte{255})) - childNode1.setValue(maybe.Some([]byte("value1"))) - - childNode2 := newNode(ToKey([]byte{237})) - childNode2.setValue(maybe.Some([]byte("value2"))) - - n.addChildWithID(childNode1, 4, hashNode(childNode1)) - n.addChildWithID(childNode2, 4, hashNode(childNode2)) - return n - }(), - expectedHash: "YVmbx5MZtSKuYhzvHnCqGrswQcxmozAkv7xE1vTA2EiGpWUkv", - }, - { - name: "16 children", - n: func() *node { - n := newNode(Key{}) - - for i := byte(0); i < 16; i++ { - childNode := newNode(ToKey([]byte{i << 4})) - childNode.setValue(maybe.Some([]byte("some value"))) - - n.addChildWithID(childNode, 4, hashNode(childNode)) - } - return n - }(), - expectedHash: "5YiFLL7QV3f441See9uWePi3wVKsx9fgvX5VPhU8PRxtLqhwY", - }, - } - for _, test := range tests { + for _, test := range hashNodeTests { t.Run(test.name, func(t *testing.T) { hash := hashNode(test.n) require.Equal(t, test.expectedHash, hash.String()) @@ -310,6 +600,15 @@ func TestHashNode(t *testing.T) { } } +func TestEncodeDBNode(t *testing.T) { + for _, test := range encodeDBNodeTests { + t.Run(test.name, func(t *testing.T) { + bytes := encodeDBNode(test.n) + require.Equal(t, test.expectedBytes, bytes) + }) + } +} + func TestCodecDecodeKeyLengthOverflowRegression(t *testing.T) { _, err := decodeKey(binary.AppendUvarint(nil, math.MaxInt)) require.ErrorIs(t, err, io.ErrUnexpectedEOF) @@ -336,68 +635,7 @@ func TestUintSize(t *testing.T) { } func Benchmark_HashNode(b *testing.B) { - benchmarks := []struct { - name string - n *node - }{ - { - name: "empty node", - n: newNode(Key{}), - }, - { - name: "has value", - n: func() *node { - n := newNode(Key{}) - n.setValue(maybe.Some([]byte("value1"))) - return n - }(), - }, - { - name: "has key", - n: newNode(ToKey([]byte{0, 1, 2, 3, 4, 5, 6, 7})), - }, - { - name: "1 child", - n: func() *node { - n := newNode(Key{}) - childNode := newNode(ToKey([]byte{255})) - childNode.setValue(maybe.Some([]byte("value1"))) - n.addChildWithID(childNode, 4, hashNode(childNode)) - return n - }(), - }, - { - name: "2 children", - n: func() *node { - n := newNode(Key{}) - - childNode1 := newNode(ToKey([]byte{255})) - childNode1.setValue(maybe.Some([]byte("value1"))) - - childNode2 := newNode(ToKey([]byte{237})) - childNode2.setValue(maybe.Some([]byte("value2"))) - - n.addChildWithID(childNode1, 4, hashNode(childNode1)) - n.addChildWithID(childNode2, 4, hashNode(childNode2)) - return n - }(), - }, - { - name: "16 children", - n: func() *node { - n := newNode(Key{}) - - for i := byte(0); i < 16; i++ { - childNode := newNode(ToKey([]byte{i << 4})) - childNode.setValue(maybe.Some([]byte("some value"))) - - n.addChildWithID(childNode, 4, hashNode(childNode)) - } - return n - }(), - }, - } - for _, benchmark := range benchmarks { + for _, benchmark := range hashNodeTests { b.Run(benchmark.name, func(b *testing.B) { for i := 0; i < b.N; i++ { hashNode(benchmark.n) @@ -406,6 +644,16 @@ func Benchmark_HashNode(b *testing.B) { } } +func Benchmark_EncodeDBNode(b *testing.B) { + for _, benchmark := range encodeDBNodeTests { + b.Run(benchmark.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + encodeDBNode(benchmark.n) + } + }) + } +} + func Benchmark_EncodeUint(b *testing.B) { var dst bytes.Buffer dst.Grow(binary.MaxVarintLen64)