avoid copying data when merging save points (#2584)

Saving both memory and processing, we can move entries from one
savepoint to another, specially when the target is empty as it often is
during transaction processing
This commit is contained in:
Jacek Sieka 2024-09-06 22:45:29 +02:00 committed by GitHub
parent e919f57902
commit 0a8986bc77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 71 additions and 38 deletions

View File

@ -53,8 +53,8 @@ proc buildBinary(name: string, srcDir = "./", params = "", lang = "c") =
exec "nim " & lang & " --out:build/" & name & " " & extra_params & " " & srcDir & name & ".nim"
proc test(path: string, name: string, params = "", lang = "c") =
# Verify stack usage is kept low by setting 750k stack limit in tests.
const stackLimitKiB = 750
# Verify stack usage is kept low by setting 1mb stack limit in tests.
const stackLimitKiB = 1024
when not defined(windows):
const (buildOption, runPrefix) = ("", "ulimit -s " & $stackLimitKiB & " && ")
else:

View File

@ -11,7 +11,8 @@
import
std/[tables, sets],
stint,
eth/common
eth/common,
../utils/mergeutils
type
SlotSet = HashSet[UInt256]
@ -49,12 +50,9 @@ func contains*(ac: var AccessList, address: EthAddress, slot: UInt256): bool =
ac.slots.withValue(address, val):
result = slot in val[]
proc merge*(ac: var AccessList, other: AccessList) {.inline.} =
for k, v in other.slots:
ac.slots.withValue(k, val):
val[].incl v
do:
ac.slots[k] = v
proc mergeAndReset*(ac, other: var AccessList) =
# move values in `other` to `ac`
ac.slots.mergeAndReset(other.slots)
proc add*(ac: var AccessList, address: EthAddress) =
if address notin ac.slots:

View File

@ -16,6 +16,7 @@ import
eth/common,
results,
stew/keyed_queue,
../../../utils/mergeutils,
../../../evm/code_bytes,
../../../stateless/multi_keys,
"../../.."/[constants, utils/utils],
@ -209,16 +210,12 @@ proc commit*(ac: AccountsLedgerRef, sp: LedgerSavePoint) =
doAssert not sp.parentSavepoint.isNil
ac.savePoint = sp.parentSavepoint
for k, v in sp.cache:
sp.parentSavepoint.cache[k] = v
for k, v in sp.dirty:
sp.parentSavepoint.dirty[k] = v
ac.savePoint.transientStorage.merge(sp.transientStorage)
ac.savePoint.accessList.merge(sp.accessList)
ac.savePoint.selfDestruct.incl sp.selfDestruct
ac.savePoint.logEntries.add sp.logEntries
ac.savePoint.cache.mergeAndReset(sp.cache)
ac.savePoint.dirty.mergeAndReset(sp.dirty)
ac.savePoint.transientStorage.mergeAndReset(sp.transientStorage)
ac.savePoint.accessList.mergeAndReset(sp.accessList)
ac.savePoint.selfDestruct.mergeAndReset(sp.selfDestruct)
ac.savePoint.logEntries.mergeAndReset(sp.logEntries)
sp.state = Committed
when debugAccountsLedgerRef:
@ -629,9 +626,6 @@ proc selfDestructLen*(ac: AccountsLedgerRef): int =
proc addLogEntry*(ac: AccountsLedgerRef, log: Log) =
ac.savePoint.logEntries.add log
proc logEntries*(ac: AccountsLedgerRef): lent seq[Log] =
ac.savePoint.logEntries
proc getAndClearLogEntries*(ac: AccountsLedgerRef): seq[Log] =
swap(result, ac.savePoint.logEntries)

View File

@ -203,11 +203,6 @@ proc isTopLevelClean*(ldg: LedgerRef): bool =
result = ldg.ac.isTopLevelClean()
ldg.ifTrackApi: debug apiTxt, api, elapsed, result
proc logEntries*(ldg: LedgerRef): seq[Log] =
ldg.beginTrackApi LdgLogEntriesFn
result = ldg.ac.logEntries()
ldg.ifTrackApi: debug apiTxt, api, elapsed, result
proc makeMultiKeys*(ldg: LedgerRef): MultiKeysRef =
ldg.beginTrackApi LdgMakeMultiKeysFn
result = ldg.ac.makeMultiKeys()

View File

@ -57,7 +57,6 @@ type
LdgIsDeadAccountFn = "isDeadAccount"
LdgIsEmptyAccountFn = "isEmptyAccount"
LdgIsTopLevelCleanFn = "isTopLevelClean"
LdgLogEntriesFn = "logEntries"
LdgMakeMultiKeysFn = "makeMultiKeys"
LdgPersistFn = "persist"
LdgRipemdSpecialFn = "ripemdSpecial"

View File

@ -11,7 +11,8 @@
import
tables,
stint,
eth/common
eth/common,
../utils/mergeutils
type
StorageTable = ref object
@ -24,9 +25,8 @@ type
# Private helpers
#######################################################################
proc merge(a, b: StorageTable) =
for k, v in b.map:
a.map[k] = v
proc mergeAndReset*(a, b: StorageTable) =
a.map.mergeAndReset(b.map)
#######################################################################
# Public functions
@ -58,12 +58,8 @@ proc setStorage*(ac: var TransientStorage,
table.map[slot] = value
proc merge*(ac: var TransientStorage, other: TransientStorage) =
for k, v in other.map:
ac.map.withValue(k, val):
val[].merge(v)
do:
ac.map[k] = v
proc mergeAndReset*(ac, other: var TransientStorage) =
ac.map.mergeAndReset(other.map)
proc clear*(ac: var TransientStorage) {.inline.} =
ac.map.clear()

View File

@ -0,0 +1,51 @@
# Nimbus
# Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
{.push raises: [], gcsafe.}
import
std/[tables, sets]
# Utilities for merging source data into a target taking care to move data and
# leave the source empty
func mergeAndReset*(tgt, src: var auto) =
tgt = move(src)
func mergeAndReset*(tgt, src: var seq) =
mixin mergeAndReset
if tgt.len == 0:
swap(tgt, src)
else:
let tlen = tgt.len
tgt.setLen(tgt.len + src.len)
for i, sv in src.mpairs():
mergeAndReset(tgt[tlen + i], sv)
src.reset()
func mergeAndReset*(tgt, src: var HashSet) =
mixin mergeAndReset
if tgt.len == 0:
swap(tgt, src)
else:
for sv in src.items():
tgt.incl(sv)
src.reset()
func mergeAndReset*(tgt, src: var Table) =
mixin mergeAndReset
if tgt.len == 0:
swap(tgt, src)
else:
for k, sv in src.mpairs():
tgt.withValue(k, tv):
mergeAndReset(tv[], sv)
do:
tgt[k] = move(sv)
src.reset()