mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +00:00
pathdb: improve perf by separating nodes map
remove comments remove comments
This commit is contained in:
parent
d342f76232
commit
5afd54abb3
1 changed files with 145 additions and 65 deletions
|
|
@ -37,7 +37,8 @@ import (
|
||||||
// the combined trie node set from several aggregated state transitions.
|
// the combined trie node set from several aggregated state transitions.
|
||||||
type nodeSet struct {
|
type nodeSet struct {
|
||||||
size uint64 // aggregated size of the trie node
|
size uint64 // aggregated size of the trie node
|
||||||
nodes map[common.Hash]map[string]*trienode.Node // node set, mapped by owner and path
|
accountNodes map[string]*trienode.Node // account trie nodes, mapped by path
|
||||||
|
storageNodes map[common.Hash]map[string]*trienode.Node // storage trie nodes, mapped by owner and path
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNodeSet constructs the set with the provided dirty trie nodes.
|
// newNodeSet constructs the set with the provided dirty trie nodes.
|
||||||
|
|
@ -46,7 +47,22 @@ func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet {
|
||||||
if nodes == nil {
|
if nodes == nil {
|
||||||
nodes = make(map[common.Hash]map[string]*trienode.Node)
|
nodes = make(map[common.Hash]map[string]*trienode.Node)
|
||||||
}
|
}
|
||||||
s := &nodeSet{nodes: nodes}
|
|
||||||
|
// Create the new structure with separate maps
|
||||||
|
s := &nodeSet{
|
||||||
|
accountNodes: make(map[string]*trienode.Node),
|
||||||
|
storageNodes: make(map[common.Hash]map[string]*trienode.Node),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate the nodes to the appropriate maps
|
||||||
|
for owner, subset := range nodes {
|
||||||
|
if owner == (common.Hash{}) {
|
||||||
|
maps.Copy(s.accountNodes, subset)
|
||||||
|
} else {
|
||||||
|
s.storageNodes[owner] = maps.Clone(subset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.computeSize()
|
s.computeSize()
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
@ -54,11 +70,13 @@ func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet {
|
||||||
// computeSize calculates the database size of the held trie nodes.
|
// computeSize calculates the database size of the held trie nodes.
|
||||||
func (s *nodeSet) computeSize() {
|
func (s *nodeSet) computeSize() {
|
||||||
var size uint64
|
var size uint64
|
||||||
for owner, subset := range s.nodes {
|
|
||||||
var prefix int
|
for path, n := range s.accountNodes {
|
||||||
if owner != (common.Hash{}) {
|
size += uint64(len(n.Blob) + len(path))
|
||||||
prefix = common.HashLength // owner (32 bytes) for storage trie nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, subset := range s.storageNodes {
|
||||||
|
prefix := common.HashLength // owner (32 bytes) for storage trie nodes
|
||||||
for path, n := range subset {
|
for path, n := range subset {
|
||||||
size += uint64(prefix + len(n.Blob) + len(path))
|
size += uint64(prefix + len(n.Blob) + len(path))
|
||||||
}
|
}
|
||||||
|
|
@ -79,15 +97,19 @@ func (s *nodeSet) updateSize(delta int64) {
|
||||||
|
|
||||||
// node retrieves the trie node with node path and its trie identifier.
|
// node retrieves the trie node with node path and its trie identifier.
|
||||||
func (s *nodeSet) node(owner common.Hash, path []byte) (*trienode.Node, bool) {
|
func (s *nodeSet) node(owner common.Hash, path []byte) (*trienode.Node, bool) {
|
||||||
subset, ok := s.nodes[owner]
|
if owner == (common.Hash{}) {
|
||||||
|
// Account trie node
|
||||||
|
n, ok := s.accountNodes[string(path)]
|
||||||
|
return n, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage trie node
|
||||||
|
subset, ok := s.storageNodes[owner]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
n, ok := subset[string(path)]
|
n, ok := subset[string(path)]
|
||||||
if !ok {
|
return n, ok
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return n, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge integrates the provided dirty nodes into the set. The provided nodeset
|
// merge integrates the provided dirty nodes into the set. The provided nodeset
|
||||||
|
|
@ -97,12 +119,22 @@ func (s *nodeSet) merge(set *nodeSet) {
|
||||||
delta int64 // size difference resulting from node merging
|
delta int64 // size difference resulting from node merging
|
||||||
overwrite counter // counter of nodes being overwritten
|
overwrite counter // counter of nodes being overwritten
|
||||||
)
|
)
|
||||||
for owner, subset := range set.nodes {
|
|
||||||
var prefix int
|
// Merge account nodes
|
||||||
if owner != (common.Hash{}) {
|
for path, n := range set.accountNodes {
|
||||||
prefix = common.HashLength
|
if orig, exist := s.accountNodes[path]; !exist {
|
||||||
|
delta += int64(len(n.Blob) + len(path))
|
||||||
|
} else {
|
||||||
|
delta += int64(len(n.Blob) - len(orig.Blob))
|
||||||
|
overwrite.add(len(orig.Blob) + len(path))
|
||||||
}
|
}
|
||||||
current, exist := s.nodes[owner]
|
s.accountNodes[path] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge storage nodes
|
||||||
|
for owner, subset := range set.storageNodes {
|
||||||
|
prefix := common.HashLength
|
||||||
|
current, exist := s.storageNodes[owner]
|
||||||
if !exist {
|
if !exist {
|
||||||
for path, n := range subset {
|
for path, n := range subset {
|
||||||
delta += int64(prefix + len(n.Blob) + len(path))
|
delta += int64(prefix + len(n.Blob) + len(path))
|
||||||
|
|
@ -113,7 +145,7 @@ func (s *nodeSet) merge(set *nodeSet) {
|
||||||
// accessible even after merging. Therefore, ownership of the nodes map
|
// accessible even after merging. Therefore, ownership of the nodes map
|
||||||
// should still belong to the original layer, and any modifications to it
|
// should still belong to the original layer, and any modifications to it
|
||||||
// should be prevented.
|
// should be prevented.
|
||||||
s.nodes[owner] = maps.Clone(subset)
|
s.storageNodes[owner] = maps.Clone(subset)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for path, n := range subset {
|
for path, n := range subset {
|
||||||
|
|
@ -125,7 +157,7 @@ func (s *nodeSet) merge(set *nodeSet) {
|
||||||
}
|
}
|
||||||
current[path] = n
|
current[path] = n
|
||||||
}
|
}
|
||||||
s.nodes[owner] = current
|
s.storageNodes[owner] = current
|
||||||
}
|
}
|
||||||
overwrite.report(gcTrieNodeMeter, gcTrieNodeBytesMeter)
|
overwrite.report(gcTrieNodeMeter, gcTrieNodeBytesMeter)
|
||||||
s.updateSize(delta)
|
s.updateSize(delta)
|
||||||
|
|
@ -136,27 +168,30 @@ func (s *nodeSet) merge(set *nodeSet) {
|
||||||
func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) {
|
func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) {
|
||||||
var delta int64
|
var delta int64
|
||||||
for owner, subset := range nodes {
|
for owner, subset := range nodes {
|
||||||
current, ok := s.nodes[owner]
|
if owner == (common.Hash{}) {
|
||||||
|
// Account trie nodes
|
||||||
|
for path, n := range subset {
|
||||||
|
orig, ok := s.accountNodes[path]
|
||||||
|
if !ok {
|
||||||
|
blob := rawdb.ReadAccountTrieNode(db, []byte(path))
|
||||||
|
if bytes.Equal(blob, n.Blob) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("non-existent node (account %v) blob: %v", path, crypto.Keccak256Hash(n.Blob).Hex()))
|
||||||
|
}
|
||||||
|
s.accountNodes[path] = n
|
||||||
|
delta += int64(len(n.Blob)) - int64(len(orig.Blob))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Storage trie nodes
|
||||||
|
current, ok := s.storageNodes[owner]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("non-existent subset (%x)", owner))
|
panic(fmt.Sprintf("non-existent subset (%x)", owner))
|
||||||
}
|
}
|
||||||
for path, n := range subset {
|
for path, n := range subset {
|
||||||
orig, ok := current[path]
|
orig, ok := current[path]
|
||||||
if !ok {
|
if !ok {
|
||||||
// There is a special case in merkle tree that one child is removed
|
blob := rawdb.ReadStorageTrieNode(db, owner, []byte(path))
|
||||||
// from a fullNode which only has two children, and then a new child
|
|
||||||
// with different position is immediately inserted into the fullNode.
|
|
||||||
// In this case, the clean child of the fullNode will also be marked
|
|
||||||
// as dirty because of node collapse and expansion. In case of database
|
|
||||||
// rollback, don't panic if this "clean" node occurs which is not
|
|
||||||
// present in buffer.
|
|
||||||
var blob []byte
|
|
||||||
if owner == (common.Hash{}) {
|
|
||||||
blob = rawdb.ReadAccountTrieNode(db, []byte(path))
|
|
||||||
} else {
|
|
||||||
blob = rawdb.ReadStorageTrieNode(db, owner, []byte(path))
|
|
||||||
}
|
|
||||||
// Ignore the clean node in the case described above.
|
|
||||||
if bytes.Equal(blob, n.Blob) {
|
if bytes.Equal(blob, n.Blob) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +201,7 @@ func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[st
|
||||||
delta += int64(len(n.Blob)) - int64(len(orig.Blob))
|
delta += int64(len(n.Blob)) - int64(len(orig.Blob))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s.updateSize(delta)
|
s.updateSize(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,8 +220,22 @@ type journalNodes struct {
|
||||||
|
|
||||||
// encode serializes the content of trie nodes into the provided writer.
|
// encode serializes the content of trie nodes into the provided writer.
|
||||||
func (s *nodeSet) encode(w io.Writer) error {
|
func (s *nodeSet) encode(w io.Writer) error {
|
||||||
nodes := make([]journalNodes, 0, len(s.nodes))
|
nodes := make([]journalNodes, 0, len(s.storageNodes)+len(s.accountNodes))
|
||||||
for owner, subset := range s.nodes {
|
|
||||||
|
// Encode account nodes
|
||||||
|
if len(s.accountNodes) > 0 {
|
||||||
|
entry := journalNodes{Owner: common.Hash{}}
|
||||||
|
for path, node := range s.accountNodes {
|
||||||
|
entry.Nodes = append(entry.Nodes, journalNode{
|
||||||
|
Path: []byte(path),
|
||||||
|
Blob: node.Blob,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nodes = append(nodes, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode storage nodes
|
||||||
|
for owner, subset := range s.storageNodes {
|
||||||
entry := journalNodes{Owner: owner}
|
entry := journalNodes{Owner: owner}
|
||||||
for path, node := range subset {
|
for path, node := range subset {
|
||||||
entry.Nodes = append(entry.Nodes, journalNode{
|
entry.Nodes = append(entry.Nodes, journalNode{
|
||||||
|
|
@ -204,8 +254,24 @@ func (s *nodeSet) decode(r *rlp.Stream) error {
|
||||||
if err := r.Decode(&encoded); err != nil {
|
if err := r.Decode(&encoded); err != nil {
|
||||||
return fmt.Errorf("load nodes: %v", err)
|
return fmt.Errorf("load nodes: %v", err)
|
||||||
}
|
}
|
||||||
nodes := make(map[common.Hash]map[string]*trienode.Node)
|
|
||||||
|
// Initialize the maps
|
||||||
|
s.accountNodes = make(map[string]*trienode.Node)
|
||||||
|
s.storageNodes = make(map[common.Hash]map[string]*trienode.Node)
|
||||||
|
|
||||||
|
// Decode the nodes
|
||||||
for _, entry := range encoded {
|
for _, entry := range encoded {
|
||||||
|
if entry.Owner == (common.Hash{}) {
|
||||||
|
// Account nodes
|
||||||
|
for _, n := range entry.Nodes {
|
||||||
|
if len(n.Blob) > 0 {
|
||||||
|
s.accountNodes[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob)
|
||||||
|
} else {
|
||||||
|
s.accountNodes[string(n.Path)] = trienode.NewDeleted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Storage nodes
|
||||||
subset := make(map[string]*trienode.Node)
|
subset := make(map[string]*trienode.Node)
|
||||||
for _, n := range entry.Nodes {
|
for _, n := range entry.Nodes {
|
||||||
if len(n.Blob) > 0 {
|
if len(n.Blob) > 0 {
|
||||||
|
|
@ -214,33 +280,47 @@ func (s *nodeSet) decode(r *rlp.Stream) error {
|
||||||
subset[string(n.Path)] = trienode.NewDeleted()
|
subset[string(n.Path)] = trienode.NewDeleted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodes[entry.Owner] = subset
|
s.storageNodes[entry.Owner] = subset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.nodes = nodes
|
|
||||||
s.computeSize()
|
s.computeSize()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// write flushes nodes into the provided database batch as a whole.
|
// write flushes nodes into the provided database batch as a whole.
|
||||||
func (s *nodeSet) write(batch ethdb.Batch, clean *fastcache.Cache) int {
|
func (s *nodeSet) write(batch ethdb.Batch, clean *fastcache.Cache) int {
|
||||||
return writeNodes(batch, s.nodes, clean)
|
// Convert the separate maps back to the format expected by writeNodes
|
||||||
|
nodes := make(map[common.Hash]map[string]*trienode.Node)
|
||||||
|
|
||||||
|
// Add account nodes
|
||||||
|
if len(s.accountNodes) > 0 {
|
||||||
|
nodes[common.Hash{}] = s.accountNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add storage nodes
|
||||||
|
for owner, subset := range s.storageNodes {
|
||||||
|
nodes[owner] = subset
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeNodes(batch, nodes, clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset clears all cached trie node data.
|
// reset clears all cached trie node data.
|
||||||
func (s *nodeSet) reset() {
|
func (s *nodeSet) reset() {
|
||||||
s.nodes = make(map[common.Hash]map[string]*trienode.Node)
|
s.accountNodes = make(map[string]*trienode.Node)
|
||||||
|
s.storageNodes = make(map[common.Hash]map[string]*trienode.Node)
|
||||||
s.size = 0
|
s.size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbsize returns the approximate size of db write.
|
// dbsize returns the approximate size of db write.
|
||||||
func (s *nodeSet) dbsize() int {
|
func (s *nodeSet) dbsize() int {
|
||||||
var m int
|
var m int
|
||||||
for owner, nodes := range s.nodes {
|
|
||||||
if owner == (common.Hash{}) {
|
m += len(s.accountNodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix
|
||||||
m += len(nodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix
|
|
||||||
} else {
|
for _, nodes := range s.storageNodes {
|
||||||
m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix
|
m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return m + int(s.size)
|
return m + int(s.size)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue