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

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/database"
"github.com/holiman/uint256"
)
// ContractCodeReader defines the interface for accessing contract code.
@ -538,75 +539,134 @@ func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common
}
type readerWithBAL struct {
Reader // Reader with cache
txIndex int
postBal map[int]types.TxPostValues // Shared accoss txs and mustn't be changed
Reader // Reader with cache
txIndex int
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{
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) {
postVals, ok := r.postBal[r.txIndex-1]
if ok {
acctVal, ok := postVals[addr]
if ok {
acct, err := r.Reader.Account(addr)
if acct == nil || err != nil {
// acct is nil, just return the postAcct
return &types.StateAccount{
Nonce: acctVal.Nonce,
Balance: acctVal.Balance,
CodeHash: acctVal.CodeHash,
}, nil
}
acctVal := r.overlayAccount(addr)
// acct mustn't be modified, or it'll change the cached pre-block values in readerwithcache
nonce := acctVal.Nonce
balance := acctVal.Balance
codeHash := acct.CodeHash
acct, err := r.Reader.Account(addr)
if acct == nil || err != nil {
// acct is nil, just return the postAcct
return &types.StateAccount{
Nonce: acctVal.Nonce,
Balance: acctVal.Balance,
CodeHash: acctVal.CodeHash,
}, nil
}
if acct.Nonce > acctVal.Nonce {
nonce = acct.Nonce
}
if acctVal.Balance == nil {
balance = acct.Balance
}
// acct mustn't be modified, or it'll change the cached pre-block values in readerwithcache
nonce := acctVal.Nonce
balance := acctVal.Balance
codeHash := acct.CodeHash
// codeHash
if acctVal.Code != nil {
codeHash = crypto.Keccak256(acctVal.Code)
}
if acct.Nonce > nonce {
nonce = acct.Nonce
}
if balance == nil {
balance = acct.Balance
}
return &types.StateAccount{
Nonce: nonce,
Balance: balance,
Root: acct.Root,
CodeHash: codeHash,
}, nil
// codeHash
if acctVal.Code != nil {
codeHash = crypto.Keccak256(acctVal.Code)
}
return &types.StateAccount{
Nonce: nonce,
Balance: balance,
Root: acct.Root,
CodeHash: codeHash,
}, 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)
if err != nil {
return nil, err
snapShot := r.postSnapshot[layer]
if snapShot[addr] != nil {
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) {
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 {
return val, nil
}
}
return val, nil
}
val, err := r.Reader.Storage(addr, slot)
@ -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) {
postVals, ok := r.postBal[r.txIndex-1]
if ok {
acctVal, ok := postVals[addr]
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
}
}
acctVal := r.overlayAccount(addr)
if acctVal.Code != nil {
return acctVal.Code, nil
}
acct, err := r.Reader.Account(addr)
if err != nil || acct == nil {
return nil, fmt.Errorf("cannot get code from acct for addr:%x, err:%v", addr, err)

View file

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

View file

@ -36,6 +36,6 @@ func (v *AcctPostValues) Clone() *AcctPostValues {
Destruct: false,
CodeHash: v.CodeHash,
Root: v.Root,
Cached: false,
Cached: false,
}
}