哪种写法更省 gas

  • Ashton
  • 更新于 2023-03-15 09:08
  • 阅读 1634

对比 EXP 和 MUL 在不同场景下的 gas 消耗

0x01 有趣的问题

有人提出这么个问题,下面哪种写法更省 gas

 function mul(uint256 x) public pure returns (uint256) {
      return x * x * x;
 }

 function exp(uint256 x) public pure returns (uint256) {
      return x ** 3;
 }

0x02 如何分析

两个写法最终的差别体现在是用运算符 " * " 还是 " * " 上。 运算符 " " 最终会用到 EVM 指令 MUL 运算符 " ** " 最终会用到 EVM 指令 EXP 对于 MUL 和 EXP 两个指令的消耗,我们可以查表:https://ethereum.org/en/developers/docs/evm/opcodes/ 通过查表得知,MUL 指令一次消耗的 gas 是 5,两次 MUL 消耗的 gas 就是 10 EXP 指令的消耗有个动态计算公式

gas_cost = 10 + 50 * byte_len_exponent

" x * 3 " 所用到 EXP 指令的消耗就是 10 + 50 1 = 60

但是不是这就意味着 "mul" 这个函数比 "exp" 这个函数更省 gas 呢?

0x03 实证

把代码在 remix 运行,当输入为 100 时, “exp” 的消耗是 1052 image.png “mul” 的消耗是 1084 image.png “exp” 函数比 "mul" 更省 gas,为啥呢? 这是因为 “ x x x" 这种写法需要更多的中间指令,如果对比 "x * x" 和 "x * 2" 就会发现 "x x" 要更省 gas 了。

但是,当我们把输入改为 1000 时, “exp“ 的消耗变为 1293 image.png 而 “mul” 的消耗仍然为 1084 image.png

这是为啥呢?

Solidity 的官方 blog https://blog.soliditylang.org/2020/12/16/solidity-v0.8.0-release-announcement/ 有这么一段话:

For bases up to 306, if the exponent is smaller than a hard-coded safe upper bound, it will use the exp opcode directly. If the base or the exponent is too large, it might fall back to the loop-based implementation.

明白了么?如果基数大小超过 306,solidity 会使用 MUL 而不是 EXP 指令,消耗了比直接用 MUL 更多的 gas。 如果我们不希望 solidity 为我们做这样的处理,可以使用 assembly:

 function exp(uint256 x) public pure returns (uint256) {
      assembly {
        mstore(0x0, exp(x, 0x3))
        return (0x0, 32)
      }
    }
点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ashton
Ashton
0x53b3...c54F
专注于 EVM 和比特币生态的区块链开发者