Gas 优化指南(一) :Gas 机制原理 ¶
约 2980 个字 11 行代码 预计阅读时间 10 分钟
本篇是 Gas 优化系列的第一篇,聚焦于 Gas 机制的底层原理。
我们将从 Gas 的设计哲学出发,逐步拆解交易费用的构成、EIP-1559 定价模型、操作码成本分级、冷热访问机制,以及 SSTORE 的核心定价规则。理解这些基础知识,是后续进行 Gas 优化的前提。
一、Gas 是什么 ¶
Gas 是 EVM 的 " 燃料 ",用于衡量计算工作量。其设计目的主要包括:
- 防止无限循环:必须付费才能执行代码,从机制上阻止恶意或无穷计算
- 公平分配区块空间:区块空间有限,通过竞价机制分配执行权
- 反映真实的计算成本:操作越复杂,对网络负担越重,对应 Gas 越高
EVM 的基本计费规则:每条 EVM 指令都有固定的 Gas 成本,一笔交易的总 Gas 消耗等于所有执行指令的成本之和。
以太坊中的计量单位:
| 单位 | 相当于 | 用途 | 科学计数法 |
|---|---|---|---|
| 1 ETH | 1 Ether | 账户余额、转账金额 | 10^0 ETH |
| 1 Gwei | 0.000000001 ETH | 计算 Gas 价格 | 10^-9 ETH |
| 1 wei | 0.000000000000000001 ETH | 最小单位 | 10^-18 ETH |
wei(最小单位):合约与协议层统一使用整数运算,所有金额最终都以 wei 为单位存储和计算,避免浮点数精度问题。
gwei(最常用单位):这是日常使用最频繁的单位,主要用于表示 Gas 价格。当你在钱包(如 MetaMask)设置交易手续费时,看到的 "Gas Price" 或 "Max Priority Fee" 通常就是以 gwei 为单位。例如,"Gas Price: 20 gwei" 意味着你愿意为每个计算步骤支付 20 gwei。
ETH(标准单位):用于表示账户余额、转账金额、代币价格等宏观数值。我们平时说 " 我有一个以太币 ",指的就是 1 ETH。
二、交易费用构成 ¶
一笔交易的 Gas 消耗分为两部分:总 Gas = Intrinsic Gas + Execution Gas
Intrinsic Gas(固有成本)
Intrinsic Gas 本质上是在为 " 交易进入 EVM 执行环境之前 " 的行为付费,它是交易本身的基础成本,与合约执行无关。包括:交易签名验证、nonce 校验、账户存在性检查、calldata 字节拷贝等。
| 组成部分 | Gas 成本 |
|---|---|
| 基础费用 | 21,000 |
| 每字节零值 calldata | 4 |
| 每字节非零值 calldata | 16 |
| 创建合约额外费用 | 32,000 |
示例:一笔简单的 ETH 转账,calldata 为空,消耗 21,000 Gas。
Execution Gas(执行成本)
合约代码执行过程中,每条指令的 Gas 成本累加。
EVM 不关心你在做什么业务,它只对三件事情收费:
- 你输入了多少字节
- 你执行了多少指令
- 你是否改变了全网状态
在深入细节之前,先记住这个模型。以太坊上任何一笔交易的 Gas 消耗,都可以归入三个来源:
| 成本类型 | 来源 | 量级 |
|---|---|---|
| 存储成本 | 读写 Storage(SLOAD/SSTORE) | 100 ~ 22,100 Gas |
| 执行成本 | 运算指令(ADD/MUL/JUMP 等) | 3 ~ 10 Gas |
| 数据成本 | Calldata(交易输入数据 |
Calldata:4 Gas/ 零字节,16 Gas/ 非零字节 Log:375 基础 + 375/topic + 8/ 数据字节 |
在大多数需要写入状态的业务合约中,存储读写往往是成本大头,所以我们默认先从 Storage 找热点。但纯读合约、签名验证合约、批量操作合约可能呈现不同的成本分布,具体问题具体分析。
三、EIP-1559 费用模型 ¶
2021 年 8 月伦敦升级后,在 EIP-1559 机制下,交易费用计算方式为:
交易费用 = Gas Used × (Base Fee + Priority Fee)
| 费用类型 | 说明 |
|---|---|
| Gas Used | 交易实际消耗的 Gas 数量 |
| Base Fee | 当前区块的基础费用(单位:wei/gas 或 gwei/gas |
| Priority Fee | 用户愿意支付给验证者的最高小费(单位:wei/gas 或 gwei/gas |
需要注意:EIP-1559 ≠ Gas 优化,它只是定价模型,真正的优化发生在 EVM opcode 层。
Gas 优化发生在 opcode 层、storage 布局层、calldata 与 memory 层,而 EIP-1559 只解决 " 每单位 Gas 多少钱 " 的问题。
Max Fee 和 Max Priority Fee
为了保护用户,EIP-1559 引入了两个上限参数:
- Max Fee Per Gas:用户愿意支付的最高总费用(包括基础费用和优先费用)
- Max Priority Fee Per Gas:用户愿意支付的最高优先费用
实际支付的费用计算拆解如下:
第一步,定义有效小费:
第二步,定义最终 Gas Price:
最终费用:
这一步拆解在教学中非常重要,因为区块浏览器、RPC 返回值、EVM trace、receipt.effectiveGasPrice 全部都在使用这个中间变量。
上述过程合在一起的计算公式为:
多支付的部分会自动退还给用户。
动态调整机制
- 区块利用率 > 50%:Base Fee 上涨(最多 +12.5%)
- 区块利用率 < 50%:Base Fee 下降(最多 -12.5%)
EIP-1559 的设计是让区块在短期内可以 0.5x 到 2x 摆动,但长期稳定在 targetGasUsed 附近。
Base Fee 的调整依据是区块 gasUsed 与 targetGasUsed 的偏离程度,而不是简单的百分比规则。
费用计算示例
假设:
- Gas Used = 21,000(简单转账)
- Base Fee = 30 gwei
- Priority Fee = 2 gwei
- Max Fee = 100 gwei
- Max Priority Fee = 2 gwei
计算过程:
- 实际优先费用 = min(2 gwei, 100 gwei - 30 gwei) = 2 gwei
- 实际总费用每 Gas = min(100 gwei, 30 gwei + 2 gwei) = 32 gwei
- 交易总费用 = 21,000 × 32 gwei = 672,000 gwei = 0.000672 ETH
其中:
- 基础费用:21,000 × 30 gwei = 630,000 gwei(被销毁)
- 优先费用:21,000 × 2 gwei = 42,000 gwei(支付给验证者)
Gas 优化的直接影响
从上述公式可以看出,降低 Gas Used 可以直接按比例减少交易费用。如果能将合约的 Gas 消耗从 100,000 降低到 80,000(减少 20%
这就是为什么 Gas 优化如此重要——它直接影响到每一位用户的钱包。
四、操作码 Gas 成本分级 ¶
EVM 操作码的 Gas 成本反映了其计算复杂度:
| 等级 | Gas 范围 | 典型操作 |
|---|---|---|
| 最便宜 | 2-3 | ADD, SUB, LT, GT, POP |
| 便宜 | 3-5 | MUL, DIV, MLOAD, MSTORE |
| 中等 | 10-30 | JUMP, JUMPI, CALLDATALOAD |
| 较贵 | 100-700 | BALANCE, EXTCODESIZE, CALL(热访问) |
| 昂贵 | 2100-2600 | SLOAD, CALL(冷访问) |
| 非常昂贵 | 5000-22100 | SSTORE |
关键观察:存储操作(SLOAD/SSTORE)是最昂贵的,因为它们涉及全网状态的读写。
SLOAD 贵,因为需要访问全局状态树;SSTORE 更贵,因为会改变共识状态,影响所有全节点。Gas 本质不是 CPU 时间,而是对全网负担的定价。
当你想判断「哪行代码最贵
- 有 Storage 写入吗? → 第一优先级热点
- 有 Storage 读取吗? → 第二优先级热点
- 有外部调用吗? → 检查是否可以合并或缓存
- 有循环吗? → 检查循环内是否有上述操作
先找 SSTORE,再找 SLOAD,再找外部调用,最后才优化计算指令。
五、冷 / 热访问机制(EIP-2929)¶
2021 年柏林升级引入了 " 访问列表 " 概念,区分冷访问和热访问。
同一笔交易中:
- 首次访问某个地址或存储槽 → 冷访问(贵)
- 再次访问同一地址或存储槽 → 热访问(便宜)
冷访问贵的原因是 EVM 需要从底层数据库加载数据到内存,热访问便宜是因为数据已经在内存中了。
冷 / 热状态是交易级别的概念,不是区块级别。每笔新交易开始时,所有访问都重新变成「冷
对 storage slot 而言:
| 操作 | Gas |
|---|---|
| cold SLOAD | 2100 |
| warm SLOAD | 100 |
EIP-2929 本质是在惩罚 " 不可预测的状态访问 "。第一次访问贵,不是因为慢,而是因为节点无法提前缓存。
六、SSTORE 的核心定价(EIP-2200)¶
| 场景 | Gas 成本 | 说明 |
|---|---|---|
| 0 → 非零 | 22,100 | 创建新存储槽 |
| 非零 → 非零(不同值) | 5,000 | 修改现有值 |
| 非零 → 0 | 5,000,退款 | 清除存储槽 |
| 0/ 非零 → 相同值 | 100 | 无实际变化 |
SSTORE 的 Gas 主要由「写入前后 slot 值的变化类型」决定,而不是 cold/warm。例如:
这两个都是非 0 → 非 0 的 SSTORE,即使第二次是 warm slot,依然是 ~5000 Gas,而不是 2900。
| 概念 | 适用指令 | 决定因素 |
|---|---|---|
| cold/warm | SLOAD、slot 访问 | 本交易中是否首次访问该 slot |
| SSTORE 执行成本 | SSTORE | slot 的「原始值 → 新值」变化类型 |
SSTORE ≠ SLOAD + 写入,SSTORE 是一个原子语义操作,以状态变化为计价单位,而不是以指令执行为计价单位。
七、Gas 退款机制 ¶
退款机制的演进历程:
最早版本:非零→0 = 15,000 refund,refund cap = 1/2
EIP-2200:refund cap 降为 1/5
EIP-3529:非零→0 refund 从 15,000 → 4,800,SELFDESTRUCT refund 移除
在 London 升级后的 EIP-3529 中,SSTORE 从非零值写为 0 时仍可获得 Gas 退款,但退款金额从原本的 15,000 Gas 大幅削减为 4,800 Gas。同时,单笔交易中可实际获得的退款总量仍受退款上限限制,即不超过该交易实际消耗 Gas(gasUsed)的 20%。
EIP-3529 后的规则
| 操作 | 退款额度 |
|---|---|
| SSTORE 清零(非零 → 0) | 4,800 |
| SELFDESTRUCT | 不再有退款 |
退款上限:最多退还交易总 Gas 的 20%。
为什么有退款上限?
历史上出现过「Gas Token」套利:在 Gas 便宜时写入大量存储,在 Gas 贵时清除获取退款。EIP-3529 将退款上限从 50% 降到 20%,基本堵死了这条路。
退款不是「返现
不要为了退款而刻意清零。清零的 5,000 Gas 成本 + 4,800 退款 = 净成本 200 Gas,看起来很便宜。但如果下次还要用这个槽,你又要付 22,100 Gas 从 0 写入非零。只有当你确定这个存储槽「永远不会再用」时,清零才是划算的。
八、Gas 消耗预测心法 ¶
判断一段代码是否昂贵,只问三个问题:
- 是否写 storage
- 是否首次访问 slot
- 是否写入非零值
三者同时满足,必然是 Gas hotspot。
再例如:for 循环本身几乎不贵,真正昂贵的是循环里的 SLOAD 与 SSTORE。memory 扩展虽有二次项,但仍远便宜于 storage。
这类经验规则的价值,远高于任何表格。
注意复杂性和可读性
Gas 优化通常会使代码变得更难读和更复杂。一个好的工程师必须在主观上权衡哪些优化是值得的,哪些不是。
小结 ¶
本篇介绍了 Gas 机制的核心原理:
- Gas 是 EVM 衡量计算工作量的燃料,交易费用由 Intrinsic Gas 和 Execution Gas 两部分构成。
- EIP-1559 定价模型通过 Base Fee 和 Priority Fee 计算最终费用,但真正的 Gas 优化发生在 opcode 层。
- 在所有操作中,存储读写(SLOAD/SSTORE)是最昂贵的,其中从 0 写入非零值的成本高达 22,100 Gas。
- 冷热访问机制和退款规则进一步影响实际成本。
下一篇我们将通过一个真实的链上实验,验证本篇介绍的 EIP-1559 费用计算规则,直观感受不同存储操作的 Gas 差异。
系列导航:
- 第一篇:Gas 优化指南(一
) :Gas 机制原理(本篇) - 第二篇:Gas 优化指南(二
) :EIP-1559 交易解析 - 第三篇:Gas 优化指南(三
) :存储优化 - 第四篇:Gas 优化指南(四
) :计算优化 - 第五篇:Gas 优化指南(五
) :Gas 评估与测量