From bd7e1fe2e5e1551de5785dd44e18f7eaaf3fcd8e Mon Sep 17 00:00:00 2001 From: jangko Date: Mon, 15 Jun 2020 13:28:08 +0700 Subject: [PATCH] reuse VMState and AccountsCache for better performance --- nimbus/db/accounts_cache.nim | 32 ++++++++++++++++++++++++++++---- nimbus/p2p/executor.nim | 4 ++-- nimbus/vm_state.nim | 15 +++++++++++++++ nimbus/vm_types.nim | 1 + 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/nimbus/db/accounts_cache.nim b/nimbus/db/accounts_cache.nim index 5179c6e48..3f2072467 100644 --- a/nimbus/db/accounts_cache.nim +++ b/nimbus/db/accounts_cache.nim @@ -33,6 +33,7 @@ type trie: SecureHexaryTrie savePoint: SavePoint witnessCache: Table[EthAddress, WitnessData] + isDirty: bool ReadOnlyStateDB* = distinct AccountsCache @@ -46,7 +47,17 @@ type cache: Table[EthAddress, RefAccount] state: TransactionState -const emptyAcc = newAccount() +const + emptyAcc = newAccount() + + resetFlags = { + IsDirty, + IsNew, + IsTouched, + IsClone, + CodeChanged, + StorageChanged + } proc beginSavepoint*(ac: var AccountsCache): SavePoint {.gcsafe.} @@ -66,7 +77,7 @@ proc rootHash*(ac: AccountsCache): KeccakHash = # make sure all savepoint already committed doAssert(ac.savePoint.parentSavePoint.isNil) # make sure all cache already committed - doAssert(ac.savePoint.cache.len == 0) + doAssert(ac.isDirty == false) ac.trie.rootHash proc beginSavepoint*(ac: var AccountsCache): SavePoint = @@ -252,6 +263,7 @@ proc persistStorage(acc: RefAccount, db: TrieDatabaseRef) = acc.account.storageRoot = accountTrie.rootHash proc makeDirty(ac: AccountsCache, address: EthAddress, cloneStorage = true): RefAccount = + ac.isDirty = true result = ac.getAccount(address) if address in ac.savePoint.cache: # it's already in latest savepoint @@ -391,9 +403,11 @@ proc deleteAccount*(ac: var AccountsCache, address: EthAddress) = let acc = ac.getAccount(address) acc.kill() -proc persist*(ac: var AccountsCache) = +proc persist*(ac: var AccountsCache, clearCache: bool = true) = # make sure all savepoint already committed doAssert(ac.savePoint.parentSavePoint.isNil) + var cleanAccounts = initHashSet[EthAddress]() + for address, acc in ac.savePoint.cache: case acc.persistMode() of Update: @@ -406,9 +420,19 @@ proc persist*(ac: var AccountsCache) = ac.trie.put address, rlp.encode(acc.account) of Remove: ac.trie.del address + if not clearCache: + cleanAccounts.incl address of DoNothing: discard - ac.savePoint.cache.clear() + + acc.flags = acc.flags - resetFlags + + if clearCache: + ac.savePoint.cache.clear() + else: + for x in cleanAccounts: + ac.savePoint.cache.del x + ac.isDirty = false iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) = # beware that if the account not persisted, diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index 9e1dc9eda..2b0cc6a3b 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -44,7 +44,7 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta if vmState.generateWitness: vmState.accountDb.collectWitnessData() - vmState.accountDb.persist() + vmState.accountDb.persist(clearCache = false) type # TODO: these types need to be removed @@ -146,7 +146,7 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v db.addBalance(header.coinbase, mainReward) if vmState.generateWitness: db.collectWitnessData() - db.persist() + db.persist(ClearCache in vmState.flags) let stateDb = vmState.accountDb if header.stateRoot != stateDb.rootHash: diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index 7fed8e753..f59d02b1b 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -43,6 +43,12 @@ proc newBaseVMState*(prevStateRoot: Hash256, header: BlockHeader, new result result.init(prevStateRoot, header, chainDB, tracerFlags) +proc newBaseVMState*(prevStateRoot: Hash256, + chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}): BaseVMState = + new result + var header: BlockHeader + result.init(prevStateRoot, header, chainDB, tracerFlags) + proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt, forkOverride=none(Fork)) = ## this proc will be called each time a new transaction ## is going to be executed @@ -55,6 +61,15 @@ proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt, vmState.chainDB.config.toFork(vmState.blockHeader.blockNumber) vmState.gasCosts = vmState.fork.forkToSchedule +proc updateBlockHeader*(vmState: BaseVMState, header: BlockHeader) = + vmState.blockHeader = header + vmState.touchedAccounts.clear() + vmState.suicides.clear() + if EnableTracing in vmState.tracer.flags: + vmState.tracer.initTracer(vmState.tracer.flags) + vmState.logEntries = @[] + vmState.receipts = @[] + method blockhash*(vmState: BaseVMState): Hash256 {.base, gcsafe.} = vmState.blockHeader.hash diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 3ce3c602f..6dd3ab91b 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -19,6 +19,7 @@ type VMFlag* = enum ExecutionOK GenerateWitness + ClearCache BaseVMState* = ref object of RootObj prevHeaders* : seq[BlockHeader]