From b72371887ad1028f2ddb238866b97af664b717f3 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 22 Jun 2026 16:33:56 +0800 Subject: [PATCH] core/vm: inline the gas deduction (#35203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ``` --- core/vm/gascosts.go | 63 +++++++++++++++++++++++------------------- core/vm/interpreter.go | 13 ++++----- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/core/vm/gascosts.go b/core/vm/gascosts.go index b1756ab5fe..881575e0a8 100644 --- a/core/vm/gascosts.go +++ b/core/vm/gascosts.go @@ -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. diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ca33670163..df69c3d1fd 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -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 } }