Skip to content

Commit 7b2bab8

Browse files
committed
trie: address comments
1 parent bf2aec6 commit 7b2bab8

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

trie/database.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,10 @@ func (n *cachedNode) rlp() []byte {
163163
// or by regenerating it from the rlp encoded blob.
164164
func (n *cachedNode) obj(hash common.Hash) node {
165165
if node, ok := n.node.(rawNode); ok {
166-
return mustDecodeNode(hash[:], node)
166+
// The raw-blob format nodes are loaded from either from
167+
// clean cache or the database, they are all in their own
168+
// copy and safe to use unsafe decoder.
169+
return mustDecodeNodeUnsafe(hash[:], node)
167170
}
168171
return expandNode(hash[:], n.node)
169172
}
@@ -346,7 +349,10 @@ func (db *Database) node(hash common.Hash) node {
346349
if enc := db.cleans.Get(nil, hash[:]); enc != nil {
347350
memcacheCleanHitMeter.Mark(1)
348351
memcacheCleanReadMeter.Mark(int64(len(enc)))
349-
return mustDecodeNode(hash[:], enc)
352+
353+
// The returned value from cache is in its own copy,
354+
// safe to use mustDecodeNodeUnsafe for decoding.
355+
return mustDecodeNodeUnsafe(hash[:], enc)
350356
}
351357
}
352358
// Retrieve the node from the dirty cache if available
@@ -371,7 +377,9 @@ func (db *Database) node(hash common.Hash) node {
371377
memcacheCleanMissMeter.Mark(1)
372378
memcacheCleanWriteMeter.Mark(int64(len(enc)))
373379
}
374-
return mustDecodeNode(hash[:], enc)
380+
// The returned value from database is in its own copy,
381+
// safe to use mustDecodeNodeUnsafe for decoding.
382+
return mustDecodeNodeUnsafe(hash[:], enc)
375383
}
376384

377385
// Node retrieves an encoded cached trie node from memory. If it cannot be found

trie/node.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func (n valueNode) fstring(ind string) string {
9999
return fmt.Sprintf("%x ", []byte(n))
100100
}
101101

102+
// mustDecodeNode is a wrapper of decodeNode and panic if any error is encountered.
102103
func mustDecodeNode(hash, buf []byte) node {
103104
n, err := decodeNode(hash, buf)
104105
if err != nil {
@@ -107,8 +108,29 @@ func mustDecodeNode(hash, buf []byte) node {
107108
return n
108109
}
109110

110-
// decodeNode parses the RLP encoding of a trie node.
111+
// mustDecodeNodeUnsafe is a wrapper of decodeNodeUnsafe and panic if any error is
112+
// encountered.
113+
func mustDecodeNodeUnsafe(hash, buf []byte) node {
114+
n, err := decodeNodeUnsafe(hash, buf)
115+
if err != nil {
116+
panic(fmt.Sprintf("node %x: %v", hash, err))
117+
}
118+
return n
119+
}
120+
121+
// decodeNode parses the RLP encoding of a trie node. It will deep-copy the passed
122+
// byte slice for decoding, so it's safe to modify the byte slice afterwards. The-
123+
// decode performance of this function is not optimal, but it is suitable for most
124+
// scenarios with low performance requirements and hard to determine whether the
125+
// byte slice be modified or not.
111126
func decodeNode(hash, buf []byte) (node, error) {
127+
return decodeNodeUnsafe(hash, common.CopyBytes(buf))
128+
}
129+
130+
// decodeNodeUnsafe parses the RLP encoding of a trie node. The passed byte slice
131+
// will be directly referenced by node without bytes deep copy, so the input MUST
132+
// not be changed after.
133+
func decodeNodeUnsafe(hash, buf []byte) (node, error) {
112134
if len(buf) == 0 {
113135
return nil, io.ErrUnexpectedEOF
114136
}

trie/node_test.go

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,29 @@ func BenchmarkEncodeShortNode(b *testing.B) {
112112
}
113113
}
114114

115+
// goos: darwin
116+
// goarch: arm64
117+
// pkg: github.com/ethereum/go-ethereum/trie
118+
// BenchmarkEncodeFullNode
119+
// BenchmarkEncodeFullNode-8 4323273 284.4 ns/op 576 B/op 1 allocs/op
120+
func BenchmarkEncodeFullNode(b *testing.B) {
121+
node := &fullNode{}
122+
for i := 0; i < 16; i++ {
123+
node.Children[i] = hashNode(randBytes(32))
124+
}
125+
b.ResetTimer()
126+
b.ReportAllocs()
127+
128+
for i := 0; i < b.N; i++ {
129+
nodeToBytes(node)
130+
}
131+
}
132+
115133
// goos: darwin
116134
// goarch: arm64
117135
// pkg: github.com/ethereum/go-ethereum/trie
118136
// BenchmarkDecodeShortNode
119-
// BenchmarkDecodeShortNode-8 9125304 129.2 ns/op 109 B/op 3 allocs/op
137+
// BenchmarkDecodeShortNode-8 7925638 151.0 ns/op 157 B/op 4 allocs/op
120138
func BenchmarkDecodeShortNode(b *testing.B) {
121139
node := &shortNode{
122140
Key: []byte{0x1, 0x2},
@@ -136,26 +154,29 @@ func BenchmarkDecodeShortNode(b *testing.B) {
136154
// goos: darwin
137155
// goarch: arm64
138156
// pkg: github.com/ethereum/go-ethereum/trie
139-
// BenchmarkEncodeFullNode
140-
// BenchmarkEncodeFullNode-8 4323273 284.4 ns/op 576 B/op 1 allocs/op
141-
func BenchmarkEncodeFullNode(b *testing.B) {
142-
node := &fullNode{}
143-
for i := 0; i < 16; i++ {
144-
node.Children[i] = hashNode(randBytes(32))
157+
// BenchmarkDecodeShortNodeUnsafe
158+
// BenchmarkDecodeShortNodeUnsafe-8 9027476 128.6 ns/op 109 B/op 3 allocs/op
159+
func BenchmarkDecodeShortNodeUnsafe(b *testing.B) {
160+
node := &shortNode{
161+
Key: []byte{0x1, 0x2},
162+
Val: hashNode(randBytes(32)),
145163
}
164+
blob := nodeToBytes(node)
165+
hash := crypto.Keccak256(blob)
166+
146167
b.ResetTimer()
147168
b.ReportAllocs()
148169

149170
for i := 0; i < b.N; i++ {
150-
nodeToBytes(node)
171+
mustDecodeNodeUnsafe(hash, blob)
151172
}
152173
}
153174

154175
// goos: darwin
155176
// goarch: arm64
156177
// pkg: github.com/ethereum/go-ethereum/trie
157178
// BenchmarkDecodeFullNode
158-
// BenchmarkDecodeFullNode-8 1789374 671.4 ns/op 704 B/op 17 allocs/op
179+
// BenchmarkDecodeFullNode-8 1597462 761.9 ns/op 1280 B/op 18 allocs/op
159180
func BenchmarkDecodeFullNode(b *testing.B) {
160181
node := &fullNode{}
161182
for i := 0; i < 16; i++ {
@@ -171,3 +192,24 @@ func BenchmarkDecodeFullNode(b *testing.B) {
171192
mustDecodeNode(hash, blob)
172193
}
173194
}
195+
196+
// goos: darwin
197+
// goarch: arm64
198+
// pkg: github.com/ethereum/go-ethereum/trie
199+
// BenchmarkDecodeFullNodeUnsafe
200+
// BenchmarkDecodeFullNodeUnsafe-8 1789070 687.1 ns/op 704 B/op 17 allocs/op
201+
func BenchmarkDecodeFullNodeUnsafe(b *testing.B) {
202+
node := &fullNode{}
203+
for i := 0; i < 16; i++ {
204+
node.Children[i] = hashNode(randBytes(32))
205+
}
206+
blob := nodeToBytes(node)
207+
hash := crypto.Keccak256(blob)
208+
209+
b.ResetTimer()
210+
b.ReportAllocs()
211+
212+
for i := 0; i < b.N; i++ {
213+
mustDecodeNodeUnsafe(hash, blob)
214+
}
215+
}

0 commit comments

Comments
 (0)