在 EVM 中,Gas 主要分为三类:
storage
的读写最昂贵其中最关键的是 storage 写入,单次写入约 20,000 gas(如果从 0 改为非 0)。
优化点 | 建议做法 | 原因 |
---|---|---|
存储变量访问 | 先读入 memory 变量,再多次使用 | 减少重复 SLOAD 成本 |
| 尽量使用 | 最便宜,避免复制 |
变量打包 | 将多个 | 节省存储槽 |
固定长度数组 | 用 | 减少动态存储开销 |
循环 | 避免无限增长数组迭代 | 每次循环线性增加 Gas |
事件 | 只记录必要字段 | 日志存储在链上也要付费 |
External call | 批处理、懒执行 | 减少重复调用 |
// 差的写法
function bad(uint256 x) external {
for (uint i = 0; i < 10; i++) {
storageVar += x;
}
}
// 优化写法
function good(uint256 x) external {
uint256 tmp = storageVar;
for (uint i = 0; i < 10; i++) {
tmp += x;
}
storageVar = tmp;
}
在循环中访问
storage
十次,成本远高于一次写回。
calldata
代替 memory
// 差的写法
function sum(uint256[] memory arr) external pure returns (uint256 s) {
for (uint i = 0; i < arr.length; i++) {
s += arr[i];
}
}
// 优化写法
function sum(uint256[] calldata arr) external pure returns (uint256 s) {
for (uint i = 0; i < arr.length; i++) {
s += arr[i];
}
}
memory
会复制数组,而 calldata
直接引用,节省大量 gas。
// 差的写法
struct Bad {
uint128 a;
uint128 b;
uint128 c;
}
// 优化写法(两个 slot -> 一个 slot)
struct Good {
uint128 a;
uint128 b;
uint256 c;
}
Solidity 会将多个小于 256bit 的变量打包进一个存储槽。
// 差的写法
event Deposit(address indexed user, uint256 amount, string memo);
// 优化写法
event Deposit(address indexed user, uint256 amount);
string
会额外消耗存储空间,除非业务必须,尽量避免。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract BadContract {
uint256 public value;
function addMany(uint256 x) external {
for (uint i = 0; i < 10; i++) {
value += x; // 每次都访问 storage
}
}
}
contract GoodContract {
uint256 public value;
function addMany(uint256 x) external {
uint256 tmp = value;
for (uint i = 0; i < 10; i++) {
tmp += x;
}
value = tmp; // 只写一次 storage
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Gas.sol";
contract GasTest is Test {
BadContract bad;
GoodContract good;
function setUp() public {
bad = new BadContract();
good = new GoodContract();
}
function testGasBad() public {
bad.addMany(1);
}
function testGasGood() public {
good.addMany(1);
}
}
执行测试:
➜ tutorial git:(main) ✗ forge test --match-path test/Gas.t.sol --gas-report -vvv
[⠊] Compiling...
[⠒] Compiling 2 files with Solc 0.8.30
[⠑] Solc 0.8.30 finished in 506.84ms
Compiler run successful!
Ran 2 tests for test/Gas.t.sol:GasTest
[PASS] testGasBad() (gas: 53693)
[PASS] testGasGood() (gas: 51716)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.57ms (894.88µs CPU time)
╭----------------------------------+-----------------+-------+--------+-------+---------╮
| src/Gas.sol:BadContract Contract | | | | | |
+=======================================================================================+
| Deployment Cost | Deployment Size | | | | |
|----------------------------------+-----------------+-------+--------+-------+---------|
| 152487 | 489 | | | | |
|----------------------------------+-----------------+-------+--------+-------+---------|
| | | | | | |
|----------------------------------+-----------------+-------+--------+-------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|----------------------------------+-----------------+-------+--------+-------+---------|
| addMany | 48203 | 48203 | 48203 | 48203 | 1 |
╰----------------------------------+-----------------+-------+--------+-------+---------╯
╭-----------------------------------+-----------------+-------+--------+-------+---------╮
| src/Gas.sol:GoodContract Contract | | | | | |
+========================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| 153147 | 492 | | | | |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| | | | | | |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| addMany | 46257 | 46257 | 46257 | 46257 | 1 |
╰-----------------------------------+-----------------+-------+--------+-------+---------╯
Ran 1 test suite in 6.50ms (3.57ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
unchecked {}
避免多余溢出检查(适合已知安全场景)--gas-report
工具可以直观对比优化效果原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。