gas优化技巧

Gas 是 EVM 执行操作的单位。每条指令消耗固定的 gas,具体可以看以太坊 操作码,gas 优化目标是减少交易所需的总 gas,提高用户体验并降低成本。

区块链数据存储位置有三种:storage(存储,也就是磁盘)、memory(内存,临时的)、calldata(只读数据)

常见优化技巧

减少对 storage 操作

  • 首次读取 storage 需 2100 gas(后续是热读取需 100 gas),而读取 memory 仅 3 gas。推荐多次访问同一存储数据时,将其缓存到 memory 以减少 SLOAD 次数。

  • 首次初始化写入 storage 的成本高达 20,000 gas,修改 storage 中已有的存储值成本是 2,900 - 5,000;storage成本远高于memory,所以能用 memory 就不要用 storage

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
 // 公共状态变量,存储在storage中
 uint256 public totalScore;

 // ❌ 非优化写法
 function updateScore(uint256[] memory scores) public {
     for (uint256 i = 0; i < scores.length; i++) {
         // 每一轮循环都在读写storage
         totalScore += scores[i];
     }
 }

 // ✅ 优化写法
 function updateScore(uint256[] memory scores) public {
     // 1. 仅读取一次storage到内存 (SLOAD)
     uint256 tempScore = totalScore;
     for (uint256 i = 0; i < scores.length; i++) {
         // 2. 在内存中操作 (MLOAD/MSTORE),每次仅需 3 Gas
         tempScore += scores[i];
     }
     // 3. 将最终结果写回storage (SSTORE),仅需一次写存储
     totalScore = tempScore;
 }

使用位压缩(Bit Packing)

EVM 存储是以 32 字节(256 位)为基本单位的。定义一个 uint256 会占满一整个插槽;定义一个 uint8,它只需要 1 字节,但如果不进行压缩,剩下的 31 字节就会被浪费掉。位压缩的目的就是通过多个连续小尺寸变量,让它们在物理上存储在同一个插槽内。

示例:

1
2
3
4
5
6
struct Packed {
  //占1字节
    uint8 a;
  //占3字节
    uint24 b;
}

循环优化

减少不必要的运算,如 array.length 缓存到变量中定义在循环外,而非每次都在循环中反复定义。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ❌ 非优化
for (uint256 i = 0; i < arr.length; i++) {
    uint256 len = arr.length;
    ...
}
// ✅ 优化
uint256 len = arr.length;
for (uint i = 0; i < len; ++i) {
    ...
}

函数可见性选择

externalpublic 更节省 gas,适用于仅被外部调用的函数。

由于 public 函数既可以被外部调用,也可以被合约内部函数调用。为了兼容内部调用,Solidity 编译器通常会将 calldata 中的参数**拷贝到内存(Memory)**中,这样就会消耗 gas。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计