diff --git a/core/blockchain.go b/core/blockchain.go index 38e4c3b13e..c056e8539f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2290,7 +2290,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness // Disable tracing for prefetcher executions. vmCfg := bc.vmConfig vmCfg.Tracer = nil - go bc.prefetcher.Prefetch(block, throwaway, &vmCfg, interruptCh) + go bc.prefetcher.Prefetch(block.Transactions(), block.Header(), block.GasLimit(), throwaway, &vmCfg, interruptCh) // 2.do trie prefetch for MPT trie node cache // it is for the big state trie tree, prefetch based on transaction's From/To address. diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index c5be14f591..6ab2ee353b 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -44,24 +44,22 @@ func NewStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The -// only goal is to pre-cache transaction signatures and state trie nodes. -func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) { +// only goal is to warm the state caches. +func (p *statePrefetcher) Prefetch(transactions types.Transactions, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) { var ( - header = block.Header() signer = types.MakeSigner(p.config, header.Number, header.Time) ) - transactions := block.Transactions() txChan := make(chan int, prefetchThread) - // No need to execute the first batch, since the main processor will do it. + for i := 0; i < prefetchThread; i++ { go func() { newStatedb := statedb.CopyDoPrefetch() if !p.config.IsHertzfix(header.Number) { newStatedb.EnableWriteOnSharedStorage() } - gaspool := new(GasPool).AddGas(block.GasLimit()) - blockContext := NewEVMBlockContext(header, p.chain, nil) - evm := vm.NewEVM(blockContext, newStatedb, p.config, *cfg) + + gaspool := new(GasPool).AddGas(gasLimit) + evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), newStatedb, p.config, *cfg) // Iterate over and process the individual transactions for { select { @@ -69,11 +67,12 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c tx := transactions[txIndex] // Convert the transaction into an executable message and pre-cache its sender msg, err := TransactionToMessage(tx, signer, header.BaseFee) - msg.SkipNonceChecks = true - msg.SkipFromEOACheck = true if err != nil { return // Also invalid block, bail out } + // Disable the nonce check + msg.SkipNonceChecks = true + newStatedb.SetTxContext(tx.Hash(), txIndex) // We attempt to apply a transaction. The goal is not to execute // the transaction successfully, rather to warm up touched data slots. @@ -99,36 +98,36 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c // PrefetchMining processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The -// only goal is to pre-cache transaction signatures and snapshot clean state. Only used for mining stage +// only goal is to warm the state caches. Only used for mining stage. func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) { var signer = types.MakeSigner(p.config, header.Number, header.Time) txCh := make(chan *types.Transaction, 2*prefetchThread) for i := 0; i < prefetchThread; i++ { go func(startCh <-chan *types.Transaction, stopCh <-chan struct{}) { - idx := 0 newStatedb := statedb.CopyDoPrefetch() - if !p.config.IsHertzfix(header.Number) { + if !p.config.IsHertzfix(header.Number) { // need in local env before Hertzfix hard fork newStatedb.EnableWriteOnSharedStorage() } - gaspool := new(GasPool).AddGas(gasLimit) - blockContext := NewEVMBlockContext(header, p.chain, nil) - evm := vm.NewEVM(blockContext, newStatedb, p.config, cfg) + + evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), newStatedb, p.config, cfg) + idx := 0 // Iterate over and process the individual transactions for { select { case tx := <-startCh: // Convert the transaction into an executable message and pre-cache its sender msg, err := TransactionToMessage(tx, signer, header.BaseFee) - msg.SkipNonceChecks = true - msg.SkipFromEOACheck = true if err != nil { return // Also invalid block, bail out } + // Disable the nonce check + msg.SkipNonceChecks = true + idx++ newStatedb.SetTxContext(tx.Hash(), idx) - ApplyMessage(evm, msg, gaspool) - gaspool = new(GasPool).AddGas(gasLimit) + ApplyMessage(evm, msg, new(GasPool).AddGas(gasLimit)) + case <-stopCh: return } diff --git a/core/state_prefetcher_test.go b/core/state_prefetcher_test.go index df7842d070..cb1ac1d326 100644 --- a/core/state_prefetcher_test.go +++ b/core/state_prefetcher_test.go @@ -62,7 +62,7 @@ func TestPrefetchLeaking(t *testing.T) { Track(ctx, t, func(ctx context.Context) { close(inter) - go archive.prefetcher.Prefetch(block, statedb, &archive.vmConfig, inter) + go archive.prefetcher.Prefetch(block.Transactions(), block.Header(), block.GasLimit(), statedb, &archive.vmConfig, inter) time.Sleep(1 * time.Second) }) } diff --git a/core/types.go b/core/types.go index 2bc461244b..d174e3f14b 100644 --- a/core/types.go +++ b/core/types.go @@ -46,8 +46,8 @@ type TransactionsByPriceAndNonce interface { type Prefetcher interface { // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The - // only goal is to pre-cache transaction signatures and state trie nodes. - Prefetch(block *types.Block, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) + // only goal is to warm the state caches. + Prefetch(transactions types.Transactions, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) // PrefetchMining used for pre-caching transaction signatures and state trie nodes. Only used for mining stage. PrefetchMining(txs TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) } diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index d4c3412565..df6c6b891f 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -31,6 +31,8 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +const prefetchTxNumber = 100 + var ( bidPreCheckTimer = metrics.NewRegisteredTimer("bid/preCheck", nil) bidTryInterruptTimer = metrics.NewRegisteredTimer("bid/sim/tryInterrupt", nil) @@ -67,6 +69,7 @@ var ( type bidWorker interface { prepareWork(params *generateParams, witness bool) (*environment, error) etherbase() common.Address + getPrefetcher() core.Prefetcher fillTransactions(interruptCh chan int32, env *environment, stopTimer *time.Timer, bidTxs mapset.Set[common.Hash]) (err error) } @@ -754,6 +757,16 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { bidSim1stBidTimer.UpdateSince(time.UnixMilli(int64(b.chain.GetHeaderByHash(bidRuntime.bid.ParentHash).MilliTimestamp()))) } + if len(bidRuntime.bid.Txs) > prefetchTxNumber { + interruptPrefetchCh := make(chan struct{}) + defer close(interruptPrefetchCh) + throwaway := bidRuntime.env.state.CopyDoPrefetch() + // Disable tracing for prefetcher executions. + vmCfg := *b.chain.GetVMConfig() + vmCfg.Tracer = nil + go b.bidWorker.getPrefetcher().Prefetch(bidRuntime.bid.Txs, bidRuntime.env.header, gasLimit, throwaway, &vmCfg, interruptPrefetchCh) + } + // commit transactions in bid for _, tx := range bidRuntime.bid.Txs { select { diff --git a/miner/worker.go b/miner/worker.go index 602f2836b0..fcdb1a9bdd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -310,6 +310,10 @@ func (w *worker) setBestBidFetcher(fetcher bidFetcher) { w.bidFetcher = fetcher } +func (w *worker) getPrefetcher() core.Prefetcher { + return w.prefetcher +} + // setEtherbase sets the etherbase used to initialize the block coinbase field. func (w *worker) setEtherbase(addr common.Address) { w.confMu.Lock()