perf: add snapShot to postState reader

This commit is contained in:
Po 2025-06-30 12:44:38 +02:00
parent 6f4f78b568
commit 9c003a2cd8
4 changed files with 172 additions and 124 deletions

View file

@ -95,7 +95,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
defer wg.Done() defer wg.Done()
start := time.Now() start := time.Now()
statedb.PreComputePostState(block.NumberU64()) statedb.PreComputePostState(block.NumberU64(), runtime.NumCPU()/2)
PrefetchMergeBALTime += time.Since(start) PrefetchMergeBALTime += time.Since(start)
exeStart := time.Now() exeStart := time.Now()
@ -120,6 +120,8 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
blockHash = block.Hash() blockHash = block.Hash()
blockNumber = block.Number() blockNumber = block.Number()
allLogs []*types.Log allLogs []*types.Log
maxLayer = runtime.NumCPU() / 2
lenTx = len(block.Transactions())
preStateProvider PreStateProvider preStateProvider PreStateProvider
workers errgroup.Group workers errgroup.Group
@ -131,10 +133,6 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
switch preStateType { switch preStateType {
case BALPreState: case BALPreState:
{ {
err := initialdb.SetPostBAL(statedb.PostBAL(), blockNumber.Uint64())
if err != nil {
return nil, err
}
} }
case SeqPreState: // must set workers limit = 1 case SeqPreState: // must set workers limit = 1
@ -167,7 +165,8 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
{ {
cleanStatedb = initialdb.Copy() cleanStatedb = initialdb.Copy()
cleanStatedb.SetTxContext(tx.Hash(), i) cleanStatedb.SetTxContext(tx.Hash(), i)
err = cleanStatedb.SetTxBALReader(statedb) postSnapshot, postBals := statedb.PostBAL()
err = cleanStatedb.SetTxBALReader(statedb, blockNumber.Uint64(), maxLayer, lenTx, postSnapshot, postBals)
if err != nil { if err != nil {
return err return err
} }

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/database" "github.com/ethereum/go-ethereum/triedb/database"
"github.com/holiman/uint256"
) )
// ContractCodeReader defines the interface for accessing contract code. // ContractCodeReader defines the interface for accessing contract code.
@ -540,20 +541,71 @@ func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common
type readerWithBAL struct { type readerWithBAL struct {
Reader // Reader with cache Reader // Reader with cache
txIndex int txIndex int
postBal map[int]types.TxPostValues // Shared accoss txs and mustn't be changed maxLayer int
step int // Must be larger than 1
postSnapshot map[int]types.TxPostValues // Shared accoss txs and mustn't be changed
postVals map[int]types.TxPostValues
} }
func newReaderWithBAL(reader Reader, txIndex int, postBal map[int]types.TxPostValues) *readerWithBAL { func newReaderWithBAL(reader Reader, txIndex int, maxLayer int, step int, postSnapshot, postVals map[int]types.TxPostValues) *readerWithBAL {
return &readerWithBAL{ return &readerWithBAL{
reader, txIndex, postBal, reader, txIndex, maxLayer, step, postSnapshot, postVals,
}
}
func (r *readerWithBAL) overlayAccount(addr common.Address) *types.AcctPostValues {
layer := r.txIndex / r.step
var (
nonce uint64
balance *uint256.Int
code []byte
)
for i := r.txIndex - 1; i > layer*r.step-1; i-- {
postState, ok := r.postVals[i]
if ok && postState[addr] != nil {
if nonce == 0 && postState[addr].Nonce > 0 {
nonce = postState[addr].Nonce
}
if balance == nil && postState[addr].Balance != nil {
balance = postState[addr].Balance
}
if code == nil && postState[addr].Code != nil {
code = postState[addr].Code
}
if nonce > 0 && balance != nil && code != nil {
break
}
}
}
snapShot := r.postSnapshot[layer]
if snapShot[addr] != nil {
if nonce == 0 {
nonce = snapShot[addr].Nonce
}
if balance == nil {
balance = snapShot[addr].Balance
}
if code == nil {
code = snapShot[addr].Code
}
}
return &types.AcctPostValues{
Nonce: nonce,
Balance: balance,
Code: code,
} }
} }
func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error) { func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error) {
postVals, ok := r.postBal[r.txIndex-1] acctVal := r.overlayAccount(addr)
if ok {
acctVal, ok := postVals[addr]
if ok {
acct, err := r.Reader.Account(addr) acct, err := r.Reader.Account(addr)
if acct == nil || err != nil { if acct == nil || err != nil {
// acct is nil, just return the postAcct // acct is nil, just return the postAcct
@ -569,10 +621,10 @@ func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error
balance := acctVal.Balance balance := acctVal.Balance
codeHash := acct.CodeHash codeHash := acct.CodeHash
if acct.Nonce > acctVal.Nonce { if acct.Nonce > nonce {
nonce = acct.Nonce nonce = acct.Nonce
} }
if acctVal.Balance == nil { if balance == nil {
balance = acct.Balance balance = acct.Balance
} }
@ -587,27 +639,35 @@ func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error
Root: acct.Root, Root: acct.Root,
CodeHash: codeHash, CodeHash: codeHash,
}, nil }, nil
}
func (r *readerWithBAL) overlayStorage(addr common.Address, slot common.Hash) (common.Hash, bool) {
layer := r.txIndex / r.step
for i := r.txIndex - 1; i > layer*r.step-1; i-- {
postState, ok := r.postVals[i]
if ok && postState[addr] != nil {
// Shouldn't use common.Hash{} to judge where slot exist, because it might just be common.Hash{}
if val, ok := postState[addr].StorageKV[slot]; ok {
return val, true
}
} }
} }
acct, err := r.Reader.Account(addr) snapShot := r.postSnapshot[layer]
if err != nil { if snapShot[addr] != nil {
return nil, err if val, ok := snapShot[addr].StorageKV[slot]; ok {
return val, true
} }
return acct, nil }
return common.Hash{}, false
} }
func (r *readerWithBAL) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { func (r *readerWithBAL) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
postVals, ok := r.postBal[r.txIndex-1] val, ok := r.overlayStorage(addr, slot)
if ok {
acct, ok := postVals[addr]
if ok {
val, ok := acct.StorageKV[slot]
if ok { if ok {
return val, nil return val, nil
} }
}
}
val, err := r.Reader.Storage(addr, slot) val, err := r.Reader.Storage(addr, slot)
if err != nil { if err != nil {
@ -618,23 +678,11 @@ func (r *readerWithBAL) Storage(addr common.Address, slot common.Hash) (common.H
} }
func (r *readerWithBAL) Code(addr common.Address, codeHash common.Hash) ([]byte, error) { func (r *readerWithBAL) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
postVals, ok := r.postBal[r.txIndex-1] acctVal := r.overlayAccount(addr)
if ok { if acctVal.Code != nil {
acctVal, ok := postVals[addr] return acctVal.Code, nil
if ok {
code := acctVal.Code
if code != nil {
return code, nil
}
if acctVal.CodeHash != nil {
code, err := r.Reader.Code(addr, common.Hash(acctVal.CodeHash))
if err != nil {
return nil, fmt.Errorf("cannot get code from code for addr:%x, err:%v", addr, err)
}
return code, nil
}
}
} }
acct, err := r.Reader.Account(addr) acct, err := r.Reader.Account(addr)
if err != nil || acct == nil { if err != nil || acct == nil {
return nil, fmt.Errorf("cannot get code from acct for addr:%x, err:%v", addr, err) return nil, fmt.Errorf("cannot get code from acct for addr:%x, err:%v", addr, err)

View file

@ -165,7 +165,8 @@ type StateDB struct {
blockNumber uint64 blockNumber uint64
// postState after appling tx // postState after appling tx
postStates map[int]*StateDB postStates map[int]*StateDB
postBAL map[int]types.TxPostValues postSnapshot map[int]types.TxPostValues
postVals map[int]types.TxPostValues
} }
type BALType int type BALType int
@ -273,71 +274,70 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
return sdb, nil return sdb, nil
} }
func (s *StateDB) PreComputePostState(blockNumber uint64) { func (s *StateDB) PreComputePostState(blockNumber uint64, maxLayer int) {
s.blockNumber = blockNumber s.blockNumber = blockNumber
postBal := map[int]types.TxPostValues{} postVals := AllBlockTxPostValues[blockNumber]
for k, v := range AllBlockTxPostValues[blockNumber] {
// Clone the map to avoid race with MergePostBalStates postSnapshot := make(map[int]types.TxPostValues)
postBal[k] = types.TxPostValues{} lenTxWithPreSys := len(postVals) - 1
for key, val := range v { step := lenTxWithPreSys/maxLayer + 1
postBal[k][key] = val.Clone() if step == 1 {
} maxLayer = lenTxWithPreSys
} }
// -1 and len(postBal)-1 are syscalls pre and post all txs. postSnapshot[0] = postVals[-1]
for i := -1; i < len(postBal)-2; i++ { for layer := 1; layer < maxLayer; layer++ {
prevVals := postBal[i] prev := postSnapshot[layer-1]
postVals := postBal[i+1] curr := make(types.TxPostValues, len(prev))
for addr, prevAcct := range prevVals { for key, val := range prev {
if postAcct, ok := postVals[addr]; ok { curr[key] = val.Clone()
for k, v := range prevAcct.StorageKV { }
if _, exists := postAcct.StorageKV[k]; !exists {
postAcct.StorageKV[k] = v for i := (layer - 1) * step; i < layer*step && i < lenTxWithPreSys-1; i++ {
postTxVal := postVals[i]
for addr, acct := range postTxVal {
if curr[addr] == nil {
curr[addr] = &types.AcctPostValues{
StorageKV: map[common.Hash]common.Hash{},
} }
} }
if postAcct.Balance == nil { if acct.Destruct {
postAcct.Balance = prevAcct.Balance delete(curr, addr)
continue
} }
if postAcct.Code == nil { if acct.Balance != nil {
postAcct.Code = prevAcct.Code curr[addr].Balance = acct.Balance
} }
// Storage changes but nonce didn't change if acct.Code != nil {
if postAcct.Nonce == 0 { curr[addr].Code = acct.Code
postAcct.Nonce = prevAcct.Nonce
} }
} else { if acct.Nonce > curr[addr].Nonce {
postVals[addr] = prevAcct curr[addr].Nonce = acct.Nonce
}
maps.Copy(curr[addr].StorageKV, acct.StorageKV)
} }
} }
postSnapshot[layer] = curr
} }
s.postBAL = postBal s.postSnapshot = postSnapshot
s.postVals = postVals
} }
func (s *StateDB) PostBAL() map[int]types.TxPostValues { func (s *StateDB) PostBAL() (postSnapshot, postVals map[int]types.TxPostValues) {
return s.postBAL postSnapshot = s.postSnapshot
postVals = s.postVals
return
} }
func (s *StateDB) SetTxBALReader(preblockReader Reader) error { func (s *StateDB) SetTxBALReader(preblockReader Reader, blockNumber uint64, maxLayer int, lenTx int, postSnapshot, postVals map[int]types.TxPostValues) error {
if s.postBAL == nil { if postSnapshot == nil || postVals == nil {
return errors.New("cannot set bal reader without postBAL") return errors.New("cannot set bal reader without postBAL")
} }
s.reader = newReaderWithBAL(preblockReader, s.txIndex, s.postBAL) step := (lenTx+1)/maxLayer + 1
return nil s.postVals = postVals
} s.postSnapshot = postSnapshot
s.blockNumber = blockNumber
func (s *StateDB) SetBlocknumber(bn uint64) error { s.reader = newReaderWithBAL(preblockReader, s.txIndex, maxLayer, step, s.postSnapshot, s.postVals)
s.blockNumber = bn
return nil
}
func (s *StateDB) SetPostBAL(postBal map[int]types.TxPostValues, bn uint64) error {
if postBal == nil {
return errors.New("cannot set postBAL without postBAL")
}
s.blockNumber = bn
s.postBAL = postBal
s.prefetcher = nil
return nil return nil
} }
@ -1144,7 +1144,8 @@ func (s *StateDB) Copy() *StateDB {
// The update journal is not copies to avoid duplicated updates in later transactions. // The update journal is not copies to avoid duplicated updates in later transactions.
blockNumber: s.blockNumber, blockNumber: s.blockNumber,
updateJournal: newJournal(), updateJournal: newJournal(),
postBAL: s.postBAL, postSnapshot: s.postSnapshot,
postVals: s.postVals,
} }
if s.witness != nil { if s.witness != nil {
state.witness = s.witness.Copy() state.witness = s.witness.Copy()