@@ -111,6 +111,11 @@ var (
111111 errChainStopped = errors .New ("blockchain is stopped" )
112112 errInvalidOldChain = errors .New ("invalid old chain" )
113113 errInvalidNewChain = errors .New ("invalid new chain" )
114+
115+ // slowBlockProcessThreshold defines the threshold (10 Mgas per second) for
116+ // considering a block execution as slow. Detailed performance metrics will be
117+ // printed if debug mode is enabled.
118+ slowBlockProcessThreshold = float64 (10 )
114119)
115120
116121var (
@@ -198,6 +203,10 @@ type BlockChainConfig struct {
198203
199204 // StateSizeTracking indicates whether the state size tracking is enabled.
200205 StateSizeTracking bool
206+
207+ // SlowBlockThreshold is the block execution speed threshold (Mgas/s)
208+ // below which detailed statistics are logged.
209+ SlowBlockThreshold uint64
201210}
202211
203212// DefaultConfig returns the default config.
@@ -338,6 +347,7 @@ type BlockChain struct {
338347 stateSizer * state.SizeTracker // State size tracking
339348
340349 lastForkReadyAlert time.Time // Last time there was a fork readiness print out
350+ slowBlockThreshold uint64 // Block execution speed threshold (Mgas/s) below which detailed statistics are logged
341351}
342352
343353// NewBlockChain returns a fully initialised block chain using information
@@ -372,19 +382,20 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
372382 log .Info ("" )
373383
374384 bc := & BlockChain {
375- chainConfig : chainConfig ,
376- cfg : cfg ,
377- db : db ,
378- triedb : triedb ,
379- triegc : prque.New [int64 , common.Hash ](nil ),
380- chainmu : syncx .NewClosableMutex (),
381- bodyCache : lru.NewCache [common.Hash , * types.Body ](bodyCacheLimit ),
382- bodyRLPCache : lru.NewCache [common.Hash , rlp.RawValue ](bodyCacheLimit ),
383- receiptsCache : lru.NewCache [common.Hash , []* types.Receipt ](receiptsCacheLimit ),
384- blockCache : lru.NewCache [common.Hash , * types.Block ](blockCacheLimit ),
385- txLookupCache : lru.NewCache [common.Hash , txLookup ](txLookupCacheLimit ),
386- engine : engine ,
387- logger : cfg .VmConfig .Tracer ,
385+ chainConfig : chainConfig ,
386+ cfg : cfg ,
387+ db : db ,
388+ triedb : triedb ,
389+ triegc : prque.New [int64 , common.Hash ](nil ),
390+ chainmu : syncx .NewClosableMutex (),
391+ bodyCache : lru.NewCache [common.Hash , * types.Body ](bodyCacheLimit ),
392+ bodyRLPCache : lru.NewCache [common.Hash , rlp.RawValue ](bodyCacheLimit ),
393+ receiptsCache : lru.NewCache [common.Hash , []* types.Receipt ](receiptsCacheLimit ),
394+ blockCache : lru.NewCache [common.Hash , * types.Block ](blockCacheLimit ),
395+ txLookupCache : lru.NewCache [common.Hash , txLookup ](txLookupCacheLimit ),
396+ engine : engine ,
397+ logger : cfg .VmConfig .Tracer ,
398+ slowBlockThreshold : cfg .SlowBlockThreshold ,
388399 }
389400 bc .hc , err = NewHeaderChain (db , chainConfig , engine , bc .insertStopped )
390401 if err != nil {
@@ -1842,7 +1853,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
18421853 // still need re-execution to generate snapshots that are missing
18431854 case err != nil && ! errors .Is (err , ErrKnownBlock ):
18441855 stats .ignored += len (it .chain )
1845- bc .reportBlock (block , nil , err )
1856+ bc .reportBadBlock (block , nil , err )
18461857 return nil , it .index , err
18471858 }
18481859 // Track the singleton witness from this chain insertion (if any)
@@ -1910,6 +1921,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
19101921 if err != nil {
19111922 return nil , it .index , err
19121923 }
1924+ res .stats .reportMetrics ()
1925+ res .stats .logSlow (block , bc .slowBlockThreshold )
1926+
19131927 // Report the import stats before returning the various results
19141928 stats .processed ++
19151929 stats .usedGas += res .usedGas
@@ -1970,15 +1984,20 @@ type blockProcessingResult struct {
19701984 procTime time.Duration
19711985 status WriteStatus
19721986 witness * stateless.Witness
1987+ stats * ExecuteStats
19731988}
19741989
19751990func (bpr * blockProcessingResult ) Witness () * stateless.Witness {
19761991 return bpr .witness
19771992}
19781993
1994+ func (bpr * blockProcessingResult ) Stats () * ExecuteStats {
1995+ return bpr .stats
1996+ }
1997+
19791998// ProcessBlock executes and validates the given block. If there was no error
19801999// it writes the block and associated state to database.
1981- func (bc * BlockChain ) ProcessBlock (parentRoot common.Hash , block * types.Block , setHead bool , makeWitness bool ) (_ * blockProcessingResult , blockEndErr error ) {
2000+ func (bc * BlockChain ) ProcessBlock (parentRoot common.Hash , block * types.Block , setHead bool , makeWitness bool ) (result * blockProcessingResult , blockEndErr error ) {
19822001 var (
19832002 err error
19842003 startTime = time .Now ()
@@ -2012,16 +2031,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20122031 }
20132032 // Upload the statistics of reader at the end
20142033 defer func () {
2015- stats := prefetch .GetStats ()
2016- accountCacheHitPrefetchMeter .Mark (stats .AccountHit )
2017- accountCacheMissPrefetchMeter .Mark (stats .AccountMiss )
2018- storageCacheHitPrefetchMeter .Mark (stats .StorageHit )
2019- storageCacheMissPrefetchMeter .Mark (stats .StorageMiss )
2020- stats = process .GetStats ()
2021- accountCacheHitMeter .Mark (stats .AccountHit )
2022- accountCacheMissMeter .Mark (stats .AccountMiss )
2023- storageCacheHitMeter .Mark (stats .StorageHit )
2024- storageCacheMissMeter .Mark (stats .StorageMiss )
2034+ pStat := prefetch .GetStats ()
2035+ accountCacheHitPrefetchMeter .Mark (pStat .AccountHit )
2036+ accountCacheMissPrefetchMeter .Mark (pStat .AccountMiss )
2037+ storageCacheHitPrefetchMeter .Mark (pStat .StorageHit )
2038+ storageCacheMissPrefetchMeter .Mark (pStat .StorageMiss )
2039+
2040+ rStat := process .GetStats ()
2041+ accountCacheHitMeter .Mark (rStat .AccountHit )
2042+ accountCacheMissMeter .Mark (rStat .AccountMiss )
2043+ storageCacheHitMeter .Mark (rStat .StorageHit )
2044+ storageCacheMissMeter .Mark (rStat .StorageMiss )
2045+
2046+ if result != nil {
2047+ result .stats .StatePrefetchCacheStats = pStat
2048+ result .stats .StateReadCacheStats = rStat
2049+ }
20252050 }()
20262051
20272052 go func (start time.Time , throwaway * state.StateDB , block * types.Block ) {
@@ -2078,14 +2103,14 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20782103 pstart := time .Now ()
20792104 res , err := bc .processor .Process (block , statedb , bc .cfg .VmConfig )
20802105 if err != nil {
2081- bc .reportBlock (block , res , err )
2106+ bc .reportBadBlock (block , res , err )
20822107 return nil , err
20832108 }
20842109 ptime := time .Since (pstart )
20852110
20862111 vstart := time .Now ()
20872112 if err := bc .validator .ValidateState (block , statedb , res , false ); err != nil {
2088- bc .reportBlock (block , res , err )
2113+ bc .reportBadBlock (block , res , err )
20892114 return nil , err
20902115 }
20912116 vtime := time .Since (vstart )
@@ -2119,26 +2144,28 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21192144 }
21202145 }
21212146
2122- xvtime := time .Since (xvstart )
2123- proctime := time .Since (startTime ) // processing + validation + cross validation
2124-
2147+ var (
2148+ xvtime = time .Since (xvstart )
2149+ proctime = time .Since (startTime ) // processing + validation + cross validation
2150+ stats = & ExecuteStats {}
2151+ )
21252152 // Update the metrics touched during block processing and validation
2126- accountReadTimer . Update ( statedb .AccountReads ) // Account reads are complete(in processing)
2127- storageReadTimer . Update ( statedb .StorageReads ) // Storage reads are complete(in processing)
2128- if statedb .AccountLoaded != 0 {
2129- accountReadSingleTimer . Update ( statedb .AccountReads / time . Duration ( statedb . AccountLoaded ) )
2130- }
2131- if statedb . StorageLoaded != 0 {
2132- storageReadSingleTimer . Update ( statedb . StorageReads / time . Duration ( statedb .StorageLoaded ))
2133- }
2134- accountUpdateTimer . Update ( statedb . AccountUpdates ) // Account updates are complete(in validation)
2135- storageUpdateTimer . Update ( statedb . StorageUpdates ) // Storage updates are complete(in validation)
2136- accountHashTimer . Update (statedb .AccountHashes ) // Account hashes are complete(in validation )
2137- triehash := statedb .AccountHashes // The time spent on tries hashing
2138- trieUpdate := statedb . AccountUpdates + statedb . StorageUpdates // The time spent on tries update
2139- blockExecutionTimer . Update ( ptime - (statedb .AccountReads + statedb .StorageReads )) // The time spent on EVM processing
2140- blockValidationTimer . Update ( vtime - (triehash + trieUpdate )) // The time spent on block validation
2141- blockCrossValidationTimer . Update ( xvtime ) // The time spent on stateless cross validation
2153+ stats . AccountReads = statedb .AccountReads // Account reads are complete(in processing)
2154+ stats . StorageReads = statedb .StorageReads // Storage reads are complete(in processing)
2155+ stats . AccountUpdates = statedb .AccountUpdates // Account updates are complete(in validation)
2156+ stats . StorageUpdates = statedb .StorageUpdates // Storage updates are complete(in validation )
2157+ stats . AccountHashes = statedb . AccountHashes // Account hashes are complete(in validation)
2158+
2159+ stats . AccountLoaded = statedb .AccountLoaded
2160+ stats . AccountUpdated = statedb . AccountUpdated
2161+ stats . AccountDeleted = statedb . AccountDeleted
2162+ stats . StorageLoaded = statedb . StorageLoaded
2163+ stats . StorageUpdated = int (statedb .StorageUpdated . Load () )
2164+ stats . StorageDeleted = int ( statedb .StorageDeleted . Load ())
2165+
2166+ stats . Execution = ptime - (statedb .AccountReads + statedb .StorageReads ) // The time spent on EVM processing
2167+ stats . Validation = vtime - (statedb . AccountHashes + statedb . AccountUpdates + statedb . StorageUpdates ) // The time spent on block validation
2168+ stats . CrossValidation = xvtime // The time spent on stateless cross validation
21422169
21432170 // Write the block to the chain and get the status.
21442171 var (
@@ -2160,24 +2187,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21602187 }
21612188
21622189 // Update the metrics touched during block commit
2163- accountCommitTimer .Update (statedb .AccountCommits ) // Account commits are complete, we can mark them
2164- storageCommitTimer .Update (statedb .StorageCommits ) // Storage commits are complete, we can mark them
2165- snapshotCommitTimer .Update (statedb .SnapshotCommits ) // Snapshot commits are complete, we can mark them
2166- triedbCommitTimer .Update (statedb .TrieDBCommits ) // Trie database commits are complete, we can mark them
2190+ stats .AccountCommits = statedb .AccountCommits // Account commits are complete, we can mark them
2191+ stats .StorageCommits = statedb .StorageCommits // Storage commits are complete, we can mark them
2192+ stats .SnapshotCommit = statedb .SnapshotCommits // Snapshot commits are complete, we can mark them
2193+ stats .TrieDBCommit = statedb .TrieDBCommits // Trie database commits are complete, we can mark them
2194+ stats .BlockWrite = time .Since (wstart ) - max (statedb .AccountCommits , statedb .StorageCommits ) /* concurrent */ - statedb .SnapshotCommits - statedb .TrieDBCommits
21672195
2168- blockWriteTimer .Update (time .Since (wstart ) - max (statedb .AccountCommits , statedb .StorageCommits ) /* concurrent */ - statedb .SnapshotCommits - statedb .TrieDBCommits )
21692196 elapsed := time .Since (startTime ) + 1 // prevent zero division
2170- blockInsertTimer .Update (elapsed )
2171-
2172- // TODO(rjl493456442) generalize the ResettingTimer
2173- mgasps := float64 (res .GasUsed ) * 1000 / float64 (elapsed )
2174- chainMgaspsMeter .Update (time .Duration (mgasps ))
2197+ stats .TotalTime = elapsed
2198+ stats .MgasPerSecond = float64 (res .GasUsed ) * 1000 / float64 (elapsed )
21752199
21762200 return & blockProcessingResult {
21772201 usedGas : res .GasUsed ,
21782202 procTime : proctime ,
21792203 status : status ,
21802204 witness : witness ,
2205+ stats : stats ,
21812206 }, nil
21822207}
21832208
@@ -2649,8 +2674,8 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
26492674 return false
26502675}
26512676
2652- // reportBlock logs a bad block error.
2653- func (bc * BlockChain ) reportBlock (block * types.Block , res * ProcessResult , err error ) {
2677+ // reportBadBlock logs a bad block error.
2678+ func (bc * BlockChain ) reportBadBlock (block * types.Block , res * ProcessResult , err error ) {
26542679 var receipts types.Receipts
26552680 if res != nil {
26562681 receipts = res .Receipts
0 commit comments