Skip to content

Commit d27f5f7

Browse files
committed
triedb/pathdb: account shared
``` go test ./triedb/pathdb/ -bench BenchmarkAddNodes ``` > old ``` goos: linux goarch: amd64 pkg: github.com/ethereum/go-ethereum/triedb/pathdb cpu: AMD Ryzen 7 5700G with Radeon Graphics BenchmarkAddNodes/Small-100-accounts-10-nodes-16 3624 308151 ns/op 1138262 B/op 1236 allocs/op BenchmarkAddNodes/Medium-500-accounts-20-nodes-16 738 2216662 ns/op 10793841 B/op 10352 allocs/op BenchmarkAddNodes/Large-2000-accounts-40-nodes-16 44 27227664 ns/op 92272295 B/op 80897 allocs/op BenchmarkAddNodes/XLarge-5000-accounts-50-nodes-16 13 92061992 ns/op 328729686 B/op 252497 allocs/op PASS ok github.com/ethereum/go-ethereum/triedb/pathdb 20.772s ``` > new ``` goos: linux goarch: amd64 pkg: github.com/ethereum/go-ethereum/triedb/pathdb cpu: AMD Ryzen 7 5700G with Radeon Graphics BenchmarkAddNodes/Small-100-accounts-10-nodes-16 6706 232594 ns/op 673550 B/op 1215 allocs/op BenchmarkAddNodes/Medium-500-accounts-20-nodes-16 804 1292732 ns/op 5910825 B/op 10328 allocs/op BenchmarkAddNodes/Large-2000-accounts-40-nodes-16 60 20907603 ns/op 53871128 B/op 80873 allocs/op BenchmarkAddNodes/XLarge-5000-accounts-50-nodes-16 13 78489762 ns/op 208748856 B/op 252475 allocs/op PASS ok github.com/ethereum/go-ethereum/triedb/pathdb 19.420s ``` Signed-off-by: jsvisa <[email protected]>
1 parent c9cd48a commit d27f5f7

File tree

2 files changed

+106
-73
lines changed

2 files changed

+106
-73
lines changed

triedb/pathdb/lookup.go

Lines changed: 103 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import (
2727
"golang.org/x/sync/errgroup"
2828
)
2929

30-
// storageNodesShardCount is the number of shards used for storage nodes.
31-
const storageNodesShardCount = 16
30+
// trienodeShardCount is the number of shards used for trie nodes.
31+
const trienodeShardCount = 16
3232

3333
// storageKey returns a key for uniquely identifying the storage slot.
3434
func storageKey(accountHash common.Hash, slotHash common.Hash) [64]byte {
@@ -71,30 +71,31 @@ type lookup struct {
7171
storages map[[64]byte][]common.Hash
7272

7373
// accountNodes represents the mutation history for specific account
74-
// trie nodes. The key is the trie path of the node, and the value is a slice
74+
// trie nodes, distributed across 16 shards for efficiency.
75+
// The key is the trie path of the node, and the value is a slice
7576
// of **diff layer** IDs indicating where the account was modified,
7677
// with the order from oldest to newest.
77-
accountNodes map[string][]common.Hash
78+
accountNodes [trienodeShardCount]map[string][]common.Hash
7879

7980
// storageNodes represents the mutation history for specific storage
8081
// slot trie nodes, distributed across 16 shards for efficiency.
8182
// The key is the account address hash and the trie path of the node,
8283
// the value is a slice of **diff layer** IDs indicating where the
8384
// slot was modified, with the order from oldest to newest.
84-
storageNodes [storageNodesShardCount]map[trienodeKey][]common.Hash
85+
storageNodes [trienodeShardCount]map[trienodeKey][]common.Hash
8586

8687
// descendant is the callback indicating whether the layer with
8788
// given root is a descendant of the one specified by `ancestor`.
8889
descendant func(state common.Hash, ancestor common.Hash) bool
8990
}
9091

91-
// getStorageShardIndex returns the shard index for a given path
92-
func getStorageShardIndex(path string) int {
92+
// getNodeShardIndex returns the shard index for a given path
93+
func getNodeShardIndex(path string) int {
9394
if len(path) == 0 {
9495
return 0
9596
}
9697
// use the first char of the path to determine the shard index
97-
return int(path[0]) % storageNodesShardCount
98+
return int(path[0]) % trienodeShardCount
9899
}
99100

100101
// newLookup initializes the lookup structure.
@@ -108,14 +109,14 @@ func newLookup(head layer, descendant func(state common.Hash, ancestor common.Ha
108109
current = current.parentLayer()
109110
}
110111
l := &lookup{
111-
accounts: make(map[common.Hash][]common.Hash),
112-
storages: make(map[[64]byte][]common.Hash),
113-
accountNodes: make(map[string][]common.Hash),
114-
descendant: descendant,
112+
accounts: make(map[common.Hash][]common.Hash),
113+
storages: make(map[[64]byte][]common.Hash),
114+
descendant: descendant,
115115
}
116116
// Initialize all 16 storage node shards
117-
for i := 0; i < storageNodesShardCount; i++ {
117+
for i := 0; i < trienodeShardCount; i++ {
118118
l.storageNodes[i] = make(map[trienodeKey][]common.Hash)
119+
l.accountNodes[i] = make(map[string][]common.Hash)
119120
}
120121

121122
// Apply the diff layers from bottom to top
@@ -225,9 +226,10 @@ func (l *lookup) storageTip(accountHash common.Hash, slotHash common.Hash, state
225226
func (l *lookup) nodeTip(accountHash common.Hash, path string, stateID common.Hash, base common.Hash) common.Hash {
226227
var list []common.Hash
227228
if accountHash == (common.Hash{}) {
228-
list = l.accountNodes[path]
229+
shardIndex := getNodeShardIndex(path)
230+
list = l.accountNodes[shardIndex][path]
229231
} else {
230-
shardIndex := getStorageShardIndex(path) // Use only path for sharding
232+
shardIndex := getNodeShardIndex(path) // Use only path for sharding
231233
list = l.storageNodes[shardIndex][makeTrienodeKey(accountHash, path)]
232234
}
233235
for i := len(list) - 1; i >= 0; i-- {
@@ -297,14 +299,7 @@ func (l *lookup) addLayer(diff *diffLayer) {
297299
wg.Add(1)
298300
go func() {
299301
defer wg.Done()
300-
for path := range diff.nodes.accountNodes {
301-
list, exists := l.accountNodes[path]
302-
if !exists {
303-
list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool
304-
}
305-
list = append(list, state)
306-
l.accountNodes[path] = list
307-
}
302+
l.addAccountNodes(state, diff.nodes.accountNodes)
308303
}()
309304

310305
wg.Add(1)
@@ -335,43 +330,61 @@ func (l *lookup) addStorageNodes(state common.Hash, nodes map[common.Hash]map[st
335330

336331
var (
337332
wg sync.WaitGroup
338-
tasks = make([][]shardTask, storageNodesShardCount)
333+
tasks = make([][]shardTask, trienodeShardCount)
339334
)
340-
341-
// Pre-allocate work lists
342335
for accountHash, slots := range nodes {
343336
for path := range slots {
344-
shardIndex := getStorageShardIndex(path)
337+
shardIndex := getNodeShardIndex(path)
345338
tasks[shardIndex] = append(tasks[shardIndex], shardTask{
346339
accountHash: accountHash,
347340
path: path,
348341
})
349342
}
350343
}
351-
352-
// Start all workers, each handling its own shard
353-
wg.Add(storageNodesShardCount)
354-
for shardIndex := 0; shardIndex < storageNodesShardCount; shardIndex++ {
355-
go func(shardIdx int) {
344+
for shardIdx := 0; shardIdx < trienodeShardCount; shardIdx++ {
345+
taskList := tasks[shardIdx]
346+
if len(taskList) == 0 {
347+
continue
348+
}
349+
wg.Add(1)
350+
go func() {
356351
defer wg.Done()
357-
358-
taskList := tasks[shardIdx]
359-
if len(taskList) == 0 {
360-
return
361-
}
362-
363352
shard := l.storageNodes[shardIdx]
364353
for _, task := range taskList {
365354
key := makeTrienodeKey(task.accountHash, task.path)
366-
list, exists := shard[key]
367-
if !exists {
368-
list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool
369-
}
370-
list = append(list, state)
371-
shard[key] = list
355+
shard[key] = append(shard[key], state)
372356
}
357+
}()
358+
}
359+
wg.Wait()
360+
}
373361

374-
}(shardIndex)
362+
func (l *lookup) addAccountNodes(state common.Hash, nodes map[string]*trienode.Node) {
363+
defer func(start time.Time) {
364+
lookupAddTrienodeLayerTimer.UpdateSince(start)
365+
}(time.Now())
366+
367+
var (
368+
wg sync.WaitGroup
369+
tasks = make([][]string, trienodeShardCount)
370+
)
371+
for path := range nodes {
372+
shardIndex := getNodeShardIndex(path)
373+
tasks[shardIndex] = append(tasks[shardIndex], path)
374+
}
375+
for shardIdx := 0; shardIdx < trienodeShardCount; shardIdx++ {
376+
taskList := tasks[shardIdx]
377+
if len(taskList) == 0 {
378+
continue
379+
}
380+
wg.Add(1)
381+
go func() {
382+
defer wg.Done()
383+
shard := l.accountNodes[shardIdx]
384+
for _, path := range taskList {
385+
shard[path] = append(shard[path], state)
386+
}
387+
}()
375388
}
376389
wg.Wait()
377390
}
@@ -446,18 +459,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error {
446459
})
447460

448461
eg.Go(func() error {
449-
for path := range diff.nodes.accountNodes {
450-
found, list := removeFromList(l.accountNodes[path], state)
451-
if !found {
452-
return fmt.Errorf("account lookup is not found, %x, state: %x", path, state)
453-
}
454-
if len(list) != 0 {
455-
l.accountNodes[path] = list
456-
} else {
457-
delete(l.accountNodes, path)
458-
}
459-
}
460-
return nil
462+
return l.removeAccountNodes(state, diff.nodes.accountNodes)
461463
})
462464

463465
eg.Go(func() error {
@@ -473,29 +475,23 @@ func (l *lookup) removeStorageNodes(state common.Hash, nodes map[common.Hash]map
473475

474476
var (
475477
eg errgroup.Group
476-
tasks = make([][]shardTask, storageNodesShardCount)
478+
tasks = make([][]shardTask, trienodeShardCount)
477479
)
478-
479-
// Pre-allocate work lists
480480
for accountHash, slots := range nodes {
481481
for path := range slots {
482-
shardIndex := getStorageShardIndex(path)
482+
shardIndex := getNodeShardIndex(path)
483483
tasks[shardIndex] = append(tasks[shardIndex], shardTask{
484484
accountHash: accountHash,
485485
path: path,
486486
})
487487
}
488488
}
489-
490-
// Start all workers, each handling its own shard
491-
for shardIndex := 0; shardIndex < storageNodesShardCount; shardIndex++ {
492-
shardIdx := shardIndex // Capture the variable
489+
for shardIdx := 0; shardIdx < trienodeShardCount; shardIdx++ {
490+
taskList := tasks[shardIdx]
491+
if len(taskList) == 0 {
492+
continue
493+
}
493494
eg.Go(func() error {
494-
taskList := tasks[shardIdx]
495-
if len(taskList) == 0 {
496-
return nil
497-
}
498-
499495
shard := l.storageNodes[shardIdx]
500496
for _, task := range taskList {
501497
key := makeTrienodeKey(task.accountHash, task.path)
@@ -514,3 +510,40 @@ func (l *lookup) removeStorageNodes(state common.Hash, nodes map[common.Hash]map
514510
}
515511
return eg.Wait()
516512
}
513+
514+
func (l *lookup) removeAccountNodes(state common.Hash, nodes map[string]*trienode.Node) error {
515+
defer func(start time.Time) {
516+
lookupRemoveTrienodeLayerTimer.UpdateSince(start)
517+
}(time.Now())
518+
519+
var (
520+
eg errgroup.Group
521+
tasks = make([][]string, trienodeShardCount)
522+
)
523+
for path := range nodes {
524+
shardIndex := getNodeShardIndex(path)
525+
tasks[shardIndex] = append(tasks[shardIndex], path)
526+
}
527+
for shardIdx := 0; shardIdx < trienodeShardCount; shardIdx++ {
528+
taskList := tasks[shardIdx]
529+
if len(taskList) == 0 {
530+
continue
531+
}
532+
eg.Go(func() error {
533+
shard := l.accountNodes[shardIdx]
534+
for _, path := range taskList {
535+
found, list := removeFromList(shard[path], state)
536+
if !found {
537+
return fmt.Errorf("account lookup is not found, %x, state: %x", path, state)
538+
}
539+
if len(list) != 0 {
540+
shard[path] = list
541+
} else {
542+
delete(shard, path)
543+
}
544+
}
545+
return nil
546+
})
547+
}
548+
return eg.Wait()
549+
}

triedb/pathdb/lookup_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ func BenchmarkAddNodes(b *testing.B) {
7575
storageNodes := generateRandomStorageNodes(tc.accountNodeCount, tc.nodesPerAccount)
7676

7777
lookup := &lookup{
78-
accountNodes: make(map[string][]common.Hash),
7978
}
8079

8180
// Initialize all 16 storage node shards
82-
for i := 0; i < storageNodesShardCount; i++ {
81+
for i := 0; i < trienodeShardCount; i++ {
8382
lookup.storageNodes[i] = make(map[trienodeKey][]common.Hash)
83+
lookup.accountNodes[i] = make(map[string][]common.Hash)
8484
}
8585

8686
var state common.Hash
@@ -91,7 +91,7 @@ func BenchmarkAddNodes(b *testing.B) {
9191

9292
for i := 0; i < b.N; i++ {
9393
// Reset the lookup instance for each benchmark iteration
94-
for j := 0; j < storageNodesShardCount; j++ {
94+
for j := 0; j < trienodeShardCount; j++ {
9595
lookup.storageNodes[j] = make(map[trienodeKey][]common.Hash)
9696
}
9797

0 commit comments

Comments
 (0)