mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
trie: fallback to sequential mode if collapse is possible
This commit is contained in:
parent
0b673454fe
commit
a826f0c6f9
1 changed files with 38 additions and 13 deletions
51
trie/trie.go
51
trie/trie.go
|
|
@ -480,6 +480,11 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parallelUpdateThreshold is the minimum number of entries a batch must contain
|
||||||
|
// before UpdateBatch processes them concurrently; smaller batches are applied
|
||||||
|
// sequentially to avoid the goroutine overhead.
|
||||||
|
const parallelUpdateThreshold = 4
|
||||||
|
|
||||||
// UpdateBatch updates a batch of entries concurrently.
|
// UpdateBatch updates a batch of entries concurrently.
|
||||||
func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
|
func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
|
||||||
// Short circuit if the trie is already committed and unusable.
|
// Short circuit if the trie is already committed and unusable.
|
||||||
|
|
@ -492,28 +497,38 @@ func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
|
||||||
// Insert the entries sequentially if there are not too many
|
// Insert the entries sequentially if there are not too many
|
||||||
// trie nodes in the trie.
|
// trie nodes in the trie.
|
||||||
fn, ok := t.root.(*fullNode)
|
fn, ok := t.root.(*fullNode)
|
||||||
if !ok || len(keys) < 4 { // TODO(rjl493456442) the parallelism threshold should be twisted
|
if !ok || len(keys) < parallelUpdateThreshold {
|
||||||
for i, key := range keys {
|
return t.updateSequential(keys, values)
|
||||||
err := t.Update(key, values[i])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
// Group the entries by the first nibble of the (hex) key, so that each group
|
||||||
|
// can be applied to a distinct child of the root independently.
|
||||||
var (
|
var (
|
||||||
ikeys = make(map[byte][][]byte)
|
ikeys = make(map[byte][][]byte)
|
||||||
ivals = make(map[byte][][]byte)
|
ivals = make(map[byte][][]byte)
|
||||||
eg errgroup.Group
|
deleted [17]bool // child positions receiving at least one deletion
|
||||||
|
eg errgroup.Group
|
||||||
)
|
)
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
hkey := keybytesToHex(key)
|
hkey := keybytesToHex(key)
|
||||||
ikeys[hkey[0]] = append(ikeys[hkey[0]], hkey)
|
ikeys[hkey[0]] = append(ikeys[hkey[0]], hkey)
|
||||||
ivals[hkey[0]] = append(ivals[hkey[0]], values[i])
|
ivals[hkey[0]] = append(ivals[hkey[0]], values[i])
|
||||||
|
if len(values[i]) == 0 {
|
||||||
|
deleted[hkey[0]] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(keys) > 0 {
|
// If the root may be collapsed after applying the updates, fallback
|
||||||
fn.flags = t.newFlag()
|
// to the sequential mode to eliminate the additional complexity.
|
||||||
|
survivors := 0
|
||||||
|
for i, child := range &fn.Children {
|
||||||
|
if child != nil && !deleted[i] {
|
||||||
|
survivors++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if survivors < 2 {
|
||||||
|
return t.updateSequential(keys, values)
|
||||||
|
}
|
||||||
|
// Execute the trie updates in concurrent mode
|
||||||
|
fn.flags = t.newFlag()
|
||||||
for pos, ks := range ikeys {
|
for pos, ks := range ikeys {
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
vs := ivals[pos]
|
vs := ivals[pos]
|
||||||
|
|
@ -543,6 +558,16 @@ func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateSequential applies the given entries to the trie one by one.
|
||||||
|
func (t *Trie) updateSequential(keys [][]byte, values [][]byte) error {
|
||||||
|
for i, key := range keys {
|
||||||
|
if err := t.Update(key, values[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MustDelete is a wrapper of Delete and will omit any encountered error but
|
// MustDelete is a wrapper of Delete and will omit any encountered error but
|
||||||
// just print out an error message.
|
// just print out an error message.
|
||||||
func (t *Trie) MustDelete(key []byte) {
|
func (t *Trie) MustDelete(key []byte) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue