mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-02 11:11:16 +00:00
core/vm: inline the gas deduction (#35203)
This PR inlines the gas deduction by getting rid of the tracer and use `chargeRegularOnly` for the non-state opcode. It fixes a performance regression introduced by EIP-8037 PR. ``` throughput MGas/s | 184.4 (±0.3%) | 193.1 (±1.0%) | +4.7% ▲ -- | -- | -- | -- mean newPayload | 164.2 ms (±0.3%) | 156.9 ms (±1.0%) | -4.5% ▲ p50 newPayload | 154.6 ms (±0.1%) | 147.6 ms (±0.7%) | -4.5% ▲ p95 newPayload | 273.3 ms (±2.3%) | 261.6 ms (±2.4%) | -4.3% ≈ noise p99 newPayload | 403.6 ms (±4.4%) | 380.9 ms (±4.0%) | -5.6% ≈ noise ```
This commit is contained in:
parent
eea629b9b3
commit
b72371887a
2 changed files with 40 additions and 36 deletions
|
|
@ -85,43 +85,48 @@ func (g GasBudget) String() string {
|
|||
return fmt.Sprintf("<%v,%v,used=<%v,%v>>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas)
|
||||
}
|
||||
|
||||
// CanAfford reports whether the running balance can cover the given cost.
|
||||
// State-gas charges that exceed the reservoir spill into regular gas.
|
||||
func (g GasBudget) CanAfford(cost GasCosts) bool {
|
||||
if g.RegularGas < cost.RegularGas {
|
||||
return false
|
||||
}
|
||||
if cost.StateGas > g.StateGas {
|
||||
spillover := cost.StateGas - g.StateGas
|
||||
if spillover > g.RegularGas-cost.RegularGas {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Charge deducts a combined regular+state cost from the running balance and
|
||||
// updates the usage accumulators. State-gas in excess of the reservoir spills
|
||||
// into regular_gas.
|
||||
func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
||||
prior := *g
|
||||
if !g.CanAfford(cost) {
|
||||
return prior, false
|
||||
}
|
||||
// Charge regular gas
|
||||
g.RegularGas -= cost.RegularGas
|
||||
g.UsedRegularGas += cost.RegularGas
|
||||
ok := g.charge(cost)
|
||||
return prior, ok
|
||||
}
|
||||
|
||||
// Charge state gas
|
||||
if cost.StateGas > g.StateGas {
|
||||
spillover := cost.StateGas - g.StateGas
|
||||
g.StateGas = 0
|
||||
g.RegularGas -= spillover
|
||||
} else {
|
||||
g.StateGas -= cost.StateGas
|
||||
// chargeRegularOnly deducts a regular-only cost.
|
||||
func (g *GasBudget) chargeRegularOnly(r uint64) bool {
|
||||
if g.RegularGas < r {
|
||||
return false
|
||||
}
|
||||
g.RegularGas -= r
|
||||
g.UsedRegularGas += r
|
||||
return true
|
||||
}
|
||||
|
||||
// charge deducts both the state and regular cost.
|
||||
func (g *GasBudget) charge(cost GasCosts) bool {
|
||||
if g.RegularGas < cost.RegularGas {
|
||||
return false
|
||||
}
|
||||
regular := g.RegularGas - cost.RegularGas
|
||||
state := g.StateGas
|
||||
|
||||
if cost.StateGas > state {
|
||||
spillover := cost.StateGas - state
|
||||
if spillover > regular {
|
||||
return false
|
||||
}
|
||||
regular -= spillover
|
||||
state = 0
|
||||
} else {
|
||||
state -= cost.StateGas
|
||||
}
|
||||
g.RegularGas = regular
|
||||
g.StateGas = state
|
||||
g.UsedRegularGas += cost.RegularGas
|
||||
g.UsedStateGas += int64(cost.StateGas)
|
||||
return prior, true
|
||||
return true
|
||||
}
|
||||
|
||||
// AsTracing converts the GasBudget into the tracing-facing Gas vector.
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if !contract.chargeRegular(cost, nil, tracing.GasChangeIgnored) {
|
||||
if !contract.Gas.chargeRegularOnly(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
|
|
@ -222,12 +222,11 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||
}
|
||||
// EIP-8037: charge regular gas before state gas. The state charge
|
||||
// is a no-op when dynamicCost.StateGas == 0 (e.g., pre-Amsterdam).
|
||||
if !contract.chargeRegular(dynamicCost.RegularGas, nil, tracing.GasChangeIgnored) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
if !contract.chargeState(dynamicCost.StateGas, nil, tracing.GasChangeIgnored) {
|
||||
if dynamicCost.StateGas == 0 {
|
||||
if !contract.Gas.chargeRegularOnly(dynamicCost.RegularGas) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
} else if !contract.Gas.charge(dynamicCost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue