diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index 23d3e82419..a070f4b8e6 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -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 } diff --git a/core/state/reader.go b/core/state/reader.go index 4797bdf7a8..e8c9acacae 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -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) diff --git a/core/state/statedb.go b/core/state/statedb.go index d5b5abb13e..98fe52c58b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -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() diff --git a/core/types/state_bal.go b/core/types/state_bal.go index 7197a58530..e2508e14c8 100644 --- a/core/types/state_bal.go +++ b/core/types/state_bal.go @@ -36,6 +36,6 @@ func (v *AcctPostValues) Clone() *AcctPostValues { Destruct: false, CodeHash: v.CodeHash, Root: v.Root, - Cached: false, + Cached: false, } }