diff --git a/nimbus/core/executor/process_block.nim b/nimbus/core/executor/process_block.nim index b1642bcc7..3e4941f9a 100644 --- a/nimbus/core/executor/process_block.nim +++ b/nimbus/core/executor/process_block.nim @@ -124,7 +124,11 @@ proc procBlkEpilogue( if vmState.collectWitnessData: db.collectWitnessData() - db.persist(clearEmptyAccount = vmState.determineFork >= FkSpurious) + # Clearing the account cache here helps manage its size when replaying + # large ranges of blocks, implicitly limiting its size using the gas limit + db.persist( + clearEmptyAccount = vmState.determineFork >= FkSpurious, + clearCache = true) if not skipValidation: let stateDB = vmState.stateDB diff --git a/nimbus/db/ledger/accounts_ledger.nim b/nimbus/db/ledger/accounts_ledger.nim index 2190ed758..d65cab878 100644 --- a/nimbus/db/ledger/accounts_ledger.nim +++ b/nimbus/db/ledger/accounts_ledger.nim @@ -79,6 +79,9 @@ type witnessCache: Table[EthAddress, WitnessData] isDirty: bool ripemdSpecial: bool + cache: Table[EthAddress, AccountRef] + # Second-level cache for the ledger save point, which is cleared on every + # persist code: KeyedQueue[Hash256, CodeBytesRef] ## The code cache provides two main benefits: ## @@ -258,6 +261,11 @@ proc getAccount( return sp = sp.parentSavepoint + if ac.cache.pop(address, result): + # Check second-level cache + ac.savePoint.cache[address] = result + return + # not found in cache, look into state trie let rc = ac.ledger.fetch address if rc.isOk: @@ -651,7 +659,8 @@ proc clearEmptyAccounts(ac: AccountsLedgerRef) = ac.ripemdSpecial = false proc persist*(ac: AccountsLedgerRef, - clearEmptyAccount: bool = false) = + clearEmptyAccount: bool = false, + clearCache = false) = # make sure all savepoint already committed doAssert(ac.savePoint.parentSavepoint.isNil) @@ -683,6 +692,11 @@ proc persist*(ac: AccountsLedgerRef, acc.flags = acc.flags - resetFlags ac.savePoint.dirty.clear() + if clearCache: + # This overwrites the cache from the previous persist, providing a crude LRU + # scheme with little overhead + ac.cache = move(ac.savePoint.cache) + ac.savePoint.selfDestruct.clear() # EIP2929 diff --git a/nimbus/db/ledger/base.nim b/nimbus/db/ledger/base.nim index 52628ac7c..f939f6ed4 100644 --- a/nimbus/db/ledger/base.nim +++ b/nimbus/db/ledger/base.nim @@ -270,10 +270,10 @@ proc makeMultiKeys*(ldg: LedgerRef): MultiKeysRef = result = ldg.ac.makeMultiKeys() ldg.ifTrackApi: debug apiTxt, api, elapsed -proc persist*(ldg: LedgerRef, clearEmptyAccount = false) = +proc persist*(ldg: LedgerRef, clearEmptyAccount = false, clearCache = false) = ldg.beginTrackApi LdgPersistFn - ldg.ac.persist(clearEmptyAccount) - ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount + ldg.ac.persist(clearEmptyAccount, clearCache) + ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount, clearCache proc ripemdSpecial*(ldg: LedgerRef) = ldg.beginTrackApi LdgRipemdSpecialFn diff --git a/nimbus/db/opts.nim b/nimbus/db/opts.nim index 0aeb8d6db..7c63eb192 100644 --- a/nimbus/db/opts.nim +++ b/nimbus/db/opts.nim @@ -18,7 +18,7 @@ const # https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning defaultMaxOpenFiles* = 512 defaultWriteBufferSize* = 64 * 1024 * 1024 - defaultRowCacheSize* = 512 * 1024 * 1024 + defaultRowCacheSize* = 2048 * 1024 * 1024 defaultBlockCacheSize* = 256 * 1024 * 1024 type DbOptions* = object # Options that are transported to the database layer