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:
rjl493456442 2026-06-22 16:33:56 +08:00 committed by GitHub
parent eea629b9b3
commit b72371887a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 36 deletions

View file

@ -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.

View file

@ -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
}
}