mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
core/state: add GetStateAndCommittedState (#31585)
Improves the SSTORE gas calculation a bit. Previously we would pull up
the state object twice. This is okay for existing objects, since they
are cached, however non-existing objects are not cached, thus we needed
to go through all 128 diff layers as well as the disk layer twice, just
for the gas calculation
```
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
cpu: AMD Ryzen 9 5900X 12-Core Processor
│ /tmp/old.txt │ /tmp/new.txt │
│ sec/op │ sec/op vs base │
Interpreter-24 1118.0n ± 2% 602.8n ± 1% -46.09% (p=0.000 n=10)
```
---------
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
6eb212b245
commit
34f00a42f8
7 changed files with 44 additions and 14 deletions
|
|
@ -389,6 +389,15 @@ func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) commo
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStateAndCommittedState returns the current value and the original value.
|
||||||
|
func (s *StateDB) GetStateAndCommittedState(addr common.Address, hash common.Hash) (common.Hash, common.Hash) {
|
||||||
|
stateObject := s.getStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
return stateObject.getState(hash)
|
||||||
|
}
|
||||||
|
return common.Hash{}, common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
// Database retrieves the low level database supporting the lower level trie ops.
|
// Database retrieves the low level database supporting the lower level trie ops.
|
||||||
func (s *StateDB) Database() Database {
|
func (s *StateDB) Database() Database {
|
||||||
return s.db
|
return s.db
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ func (s *hookedStateDB) GetRefund() uint64 {
|
||||||
return s.inner.GetRefund()
|
return s.inner.GetRefund()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *hookedStateDB) GetStateAndCommittedState(addr common.Address, hash common.Hash) (common.Hash, common.Hash) {
|
||||||
return s.inner.GetCommittedState(addr, hash)
|
return s.inner.GetStateAndCommittedState(addr, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,8 @@ var (
|
||||||
|
|
||||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.Back(1), stack.Back(0)
|
||||||
current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
||||||
)
|
)
|
||||||
// The legacy gas metering only takes into consideration the current state
|
// The legacy gas metering only takes into consideration the current state
|
||||||
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
||||||
|
|
@ -139,7 +139,6 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
return params.NetSstoreNoopGas, nil
|
return params.NetSstoreNoopGas, nil
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
return params.NetSstoreInitGas, nil
|
return params.NetSstoreInitGas, nil
|
||||||
|
|
@ -188,15 +187,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
}
|
}
|
||||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.Back(1), stack.Back(0)
|
||||||
current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
||||||
)
|
)
|
||||||
value := common.Hash(y.Bytes32())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
return params.SloadGasEIP2200, nil
|
return params.SloadGasEIP2200, nil
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
return params.SstoreSetGasEIP2200, nil
|
return params.SstoreSetGasEIP2200, nil
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ type StateDB interface {
|
||||||
SubRefund(uint64)
|
SubRefund(uint64)
|
||||||
GetRefund() uint64
|
GetRefund() uint64
|
||||||
|
|
||||||
GetCommittedState(common.Address, common.Hash) common.Hash
|
GetStateAndCommittedState(common.Address, common.Hash) (common.Hash, common.Hash)
|
||||||
GetState(common.Address, common.Hash) common.Hash
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
SetState(common.Address, common.Hash, common.Hash) common.Hash
|
SetState(common.Address, common.Hash, common.Hash) common.Hash
|
||||||
GetStorageRoot(addr common.Address) common.Hash
|
GetStorageRoot(addr common.Address) common.Hash
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -74,3 +75,22 @@ func TestLoopInterrupt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkInterpreter(b *testing.B) {
|
||||||
|
var (
|
||||||
|
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
|
evm = NewEVM(BlockContext{BlockNumber: big.NewInt(1), Time: 1, Random: &common.Hash{}}, statedb, params.MergedTestChainConfig, Config{})
|
||||||
|
startGas uint64 = 100_000_000
|
||||||
|
value = uint256.NewInt(0)
|
||||||
|
stack = newstack()
|
||||||
|
mem = NewMemory()
|
||||||
|
contract = NewContract(common.Address{}, common.Address{}, value, startGas, nil)
|
||||||
|
)
|
||||||
|
stack.push(uint256.NewInt(123))
|
||||||
|
stack.push(uint256.NewInt(123))
|
||||||
|
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
gasSStoreEIP3529(evm, contract, stack, mem, 1234)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
}
|
}
|
||||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.peek()
|
y, x = stack.Back(1), stack.peek()
|
||||||
slot = common.Hash(x.Bytes32())
|
slot = common.Hash(x.Bytes32())
|
||||||
current = evm.StateDB.GetState(contract.Address(), slot)
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||||
cost = uint64(0)
|
cost = uint64(0)
|
||||||
)
|
)
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
|
|
@ -52,7 +52,6 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
// return params.SloadGasEIP2200, nil
|
// return params.SloadGasEIP2200, nil
|
||||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
return cost + params.SstoreSetGasEIP2200, nil
|
return cost + params.SstoreSetGasEIP2200, nil
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) co
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*dummyStatedb) GetStateAndCommittedState(common.Address, common.Hash) (common.Hash, common.Hash) {
|
||||||
|
return common.Hash{}, common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStoreCapture(t *testing.T) {
|
func TestStoreCapture(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
logger = NewStructLogger(nil)
|
logger = NewStructLogger(nil)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue