62
62
blockExecutionTimer = metrics .NewRegisteredTimer ("chain/execution" , nil )
63
63
blockWriteTimer = metrics .NewRegisteredTimer ("chain/write" , nil )
64
64
65
+ blockPrefetchExecuteTimer = metrics .NewRegisteredTimer ("chain/prefetch/executes" , nil )
66
+ blockPrefetchInterruptMeter = metrics .NewRegisteredMeter ("chain/prefetch/interrupts" , nil )
67
+
65
68
ErrNoGenesis = errors .New ("Genesis not found in chain" )
66
69
)
67
70
@@ -87,10 +90,11 @@ const (
87
90
// CacheConfig contains the configuration values for the trie caching/pruning
88
91
// that's resident in a blockchain.
89
92
type CacheConfig struct {
90
- Disabled bool // Whether to disable trie write caching (archive node)
91
- TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
92
- TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
93
- TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
93
+ TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
94
+ TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
95
+ TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
96
+ TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
97
+ TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
94
98
}
95
99
96
100
// BlockChain represents the canonical chain given a database with a genesis
@@ -126,7 +130,6 @@ type BlockChain struct {
126
130
genesisBlock * types.Block
127
131
128
132
chainmu sync.RWMutex // blockchain insertion lock
129
- procmu sync.RWMutex // block processor lock
130
133
131
134
checkpoint int // checkpoint counts towards the new checkpoint
132
135
currentBlock atomic.Value // Current head of the block chain
@@ -145,10 +148,11 @@ type BlockChain struct {
145
148
procInterrupt int32 // interrupt signaler for block processing
146
149
wg sync.WaitGroup // chain processing wait group for shutting down
147
150
148
- engine consensus.Engine
149
- processor Processor // block processor interface
150
- validator Validator // block and state validator interface
151
- vmConfig vm.Config
151
+ engine consensus.Engine
152
+ validator Validator // Block and state validator interface
153
+ prefetcher Prefetcher // Block state prefetcher interface
154
+ processor Processor // Block transaction processor interface
155
+ vmConfig vm.Config
152
156
153
157
badBlocks * lru.Cache // Bad block cache
154
158
shouldPreserve func (* types.Block ) bool // Function used to determine whether should preserve the given block.
@@ -189,8 +193,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
189
193
vmConfig : vmConfig ,
190
194
badBlocks : badBlocks ,
191
195
}
192
- bc .SetValidator (NewBlockValidator (chainConfig , bc , engine ))
193
- bc .SetProcessor (NewStateProcessor (chainConfig , bc , engine ))
196
+ bc .validator = NewBlockValidator (chainConfig , bc , engine )
197
+ bc .prefetcher = newStatePrefetcher (chainConfig , bc , engine )
198
+ bc .processor = NewStateProcessor (chainConfig , bc , engine )
194
199
195
200
var err error
196
201
bc .hc , err = NewHeaderChain (db , chainConfig , engine , bc .getProcInterrupt )
@@ -381,31 +386,13 @@ func (bc *BlockChain) CurrentFastBlock() *types.Block {
381
386
return bc .currentFastBlock .Load ().(* types.Block )
382
387
}
383
388
384
- // SetProcessor sets the processor required for making state modifications.
385
- func (bc * BlockChain ) SetProcessor (processor Processor ) {
386
- bc .procmu .Lock ()
387
- defer bc .procmu .Unlock ()
388
- bc .processor = processor
389
- }
390
-
391
- // SetValidator sets the validator which is used to validate incoming blocks.
392
- func (bc * BlockChain ) SetValidator (validator Validator ) {
393
- bc .procmu .Lock ()
394
- defer bc .procmu .Unlock ()
395
- bc .validator = validator
396
- }
397
-
398
389
// Validator returns the current validator.
399
390
func (bc * BlockChain ) Validator () Validator {
400
- bc .procmu .RLock ()
401
- defer bc .procmu .RUnlock ()
402
391
return bc .validator
403
392
}
404
393
405
394
// Processor returns the current processor.
406
395
func (bc * BlockChain ) Processor () Processor {
407
- bc .procmu .RLock ()
408
- defer bc .procmu .RUnlock ()
409
396
return bc .processor
410
397
}
411
398
@@ -722,7 +709,7 @@ func (bc *BlockChain) Stop() {
722
709
// - HEAD: So we don't need to reprocess any blocks in the general case
723
710
// - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
724
711
// - HEAD-127: So we have a hard limit on the number of blocks reexecuted
725
- if ! bc .cacheConfig .Disabled {
712
+ if ! bc .cacheConfig .TrieDirtyDisabled {
726
713
triedb := bc .stateCache .TrieDB ()
727
714
728
715
for _ , offset := range []uint64 {0 , 1 , triesInMemory - 1 } {
@@ -982,7 +969,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
982
969
triedb := bc .stateCache .TrieDB ()
983
970
984
971
// If we're running an archive node, always flush
985
- if bc .cacheConfig .Disabled {
972
+ if bc .cacheConfig .TrieDirtyDisabled {
986
973
if err := triedb .Commit (root , false ); err != nil {
987
974
return NonStatTy , err
988
975
}
@@ -1147,7 +1134,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
1147
1134
// is imported, but then new canon-head is added before the actual sidechain
1148
1135
// completes, then the historic state could be pruned again
1149
1136
func (bc * BlockChain ) insertChain (chain types.Blocks , verifySeals bool ) (int , []interface {}, []* types.Log , error ) {
1150
- // If the chain is terminating, don't even bother starting u
1137
+ // If the chain is terminating, don't even bother starting up
1151
1138
if atomic .LoadInt32 (& bc .procInterrupt ) == 1 {
1152
1139
return 0 , nil , nil , nil
1153
1140
}
@@ -1175,7 +1162,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
1175
1162
defer close (abort )
1176
1163
1177
1164
// Peek the error for the first block to decide the directing import logic
1178
- it := newInsertIterator (chain , results , bc .Validator () )
1165
+ it := newInsertIterator (chain , results , bc .validator )
1179
1166
1180
1167
block , err := it .next ()
1181
1168
@@ -1238,54 +1225,76 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
1238
1225
if parent == nil {
1239
1226
parent = bc .GetHeader (block .ParentHash (), block .NumberU64 ()- 1 )
1240
1227
}
1241
- state , err := state .New (parent .Root , bc .stateCache )
1228
+ statedb , err := state .New (parent .Root , bc .stateCache )
1242
1229
if err != nil {
1243
1230
return it .index , events , coalescedLogs , err
1244
1231
}
1245
- // Process block using the parent state as reference point.
1232
+ // If we have a followup block, run that against the current state to pre-cache
1233
+ // transactions and probabilistically some of the account/storage trie nodes.
1234
+ var followupInterrupt uint32
1235
+
1236
+ if ! bc .cacheConfig .TrieCleanNoPrefetch {
1237
+ if followup , err := it .peek (); followup != nil && err == nil {
1238
+ go func (start time.Time ) {
1239
+ throwaway , _ := state .New (parent .Root , bc .stateCache )
1240
+ bc .prefetcher .Prefetch (followup , throwaway , bc .vmConfig , & followupInterrupt )
1241
+
1242
+ blockPrefetchExecuteTimer .Update (time .Since (start ))
1243
+ if atomic .LoadUint32 (& followupInterrupt ) == 1 {
1244
+ blockPrefetchInterruptMeter .Mark (1 )
1245
+ }
1246
+ }(time .Now ())
1247
+ }
1248
+ }
1249
+ // Process block using the parent state as reference point
1246
1250
substart := time .Now ()
1247
- receipts , logs , usedGas , err := bc .processor .Process (block , state , bc .vmConfig )
1251
+ receipts , logs , usedGas , err := bc .processor .Process (block , statedb , bc .vmConfig )
1248
1252
if err != nil {
1249
1253
bc .reportBlock (block , receipts , err )
1254
+ atomic .StoreUint32 (& followupInterrupt , 1 )
1250
1255
return it .index , events , coalescedLogs , err
1251
1256
}
1252
1257
// Update the metrics touched during block processing
1253
- accountReadTimer .Update (state .AccountReads ) // Account reads are complete, we can mark them
1254
- storageReadTimer .Update (state .StorageReads ) // Storage reads are complete, we can mark them
1255
- accountUpdateTimer .Update (state .AccountUpdates ) // Account updates are complete, we can mark them
1256
- storageUpdateTimer .Update (state .StorageUpdates ) // Storage updates are complete, we can mark them
1258
+ accountReadTimer .Update (statedb .AccountReads ) // Account reads are complete, we can mark them
1259
+ storageReadTimer .Update (statedb .StorageReads ) // Storage reads are complete, we can mark them
1260
+ accountUpdateTimer .Update (statedb .AccountUpdates ) // Account updates are complete, we can mark them
1261
+ storageUpdateTimer .Update (statedb .StorageUpdates ) // Storage updates are complete, we can mark them
1257
1262
1258
- triehash := state .AccountHashes + state .StorageHashes // Save to not double count in validation
1259
- trieproc := state .AccountReads + state .AccountUpdates
1260
- trieproc += state .StorageReads + state .StorageUpdates
1263
+ triehash := statedb .AccountHashes + statedb .StorageHashes // Save to not double count in validation
1264
+ trieproc := statedb .AccountReads + statedb .AccountUpdates
1265
+ trieproc += statedb .StorageReads + statedb .StorageUpdates
1261
1266
1262
1267
blockExecutionTimer .Update (time .Since (substart ) - trieproc - triehash )
1263
1268
1264
1269
// Validate the state using the default validator
1265
1270
substart = time .Now ()
1266
- if err := bc .Validator () .ValidateState (block , state , receipts , usedGas ); err != nil {
1271
+ if err := bc .validator .ValidateState (block , statedb , receipts , usedGas ); err != nil {
1267
1272
bc .reportBlock (block , receipts , err )
1273
+ atomic .StoreUint32 (& followupInterrupt , 1 )
1268
1274
return it .index , events , coalescedLogs , err
1269
1275
}
1270
1276
proctime := time .Since (start )
1271
1277
1272
1278
// Update the metrics touched during block validation
1273
- accountHashTimer .Update (state .AccountHashes ) // Account hashes are complete, we can mark them
1274
- storageHashTimer .Update (state .StorageHashes ) // Storage hashes are complete, we can mark them
1279
+ accountHashTimer .Update (statedb .AccountHashes ) // Account hashes are complete, we can mark them
1280
+ storageHashTimer .Update (statedb .StorageHashes ) // Storage hashes are complete, we can mark them
1275
1281
1276
- blockValidationTimer .Update (time .Since (substart ) - (state .AccountHashes + state .StorageHashes - triehash ))
1282
+ blockValidationTimer .Update (time .Since (substart ) - (statedb .AccountHashes + statedb .StorageHashes - triehash ))
1277
1283
1278
1284
// Write the block to the chain and get the status.
1279
1285
substart = time .Now ()
1280
- status , err := bc .writeBlockWithState (block , receipts , state )
1286
+ status , err := bc .writeBlockWithState (block , receipts , statedb )
1281
1287
if err != nil {
1288
+ atomic .StoreUint32 (& followupInterrupt , 1 )
1282
1289
return it .index , events , coalescedLogs , err
1283
1290
}
1291
+ atomic .StoreUint32 (& followupInterrupt , 1 )
1292
+
1284
1293
// Update the metrics touched during block commit
1285
- accountCommitTimer .Update (state .AccountCommits ) // Account commits are complete, we can mark them
1286
- storageCommitTimer .Update (state .StorageCommits ) // Storage commits are complete, we can mark them
1294
+ accountCommitTimer .Update (statedb .AccountCommits ) // Account commits are complete, we can mark them
1295
+ storageCommitTimer .Update (statedb .StorageCommits ) // Storage commits are complete, we can mark them
1287
1296
1288
- blockWriteTimer .Update (time .Since (substart ) - state .AccountCommits - state .StorageCommits )
1297
+ blockWriteTimer .Update (time .Since (substart ) - statedb .AccountCommits - statedb .StorageCommits )
1289
1298
blockInsertTimer .UpdateSince (start )
1290
1299
1291
1300
switch status {
0 commit comments