对比 EXP 和 MUL 在不同场景下的 gas 消耗
有人提出这么个问题,下面哪种写法更省 gas
function mul(uint256 x) public pure returns (uint256) {
return x * x * x;
}
function exp(uint256 x) public pure returns (uint256) {
return x ** 3;
}
两个写法最终的差别体现在是用运算符 " * " 还是 " * " 上。 运算符 " " 最终会用到 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 呢?
把代码在 remix 运行,当输入为 100 时, “exp” 的消耗是 1052 “mul” 的消耗是 1084 “exp” 函数比 "mul" 更省 gas,为啥呢? 这是因为 “ x x x" 这种写法需要更多的中间指令,如果对比 "x * x" 和 "x * 2" 就会发现 "x x" 要更省 gas 了。
但是,当我们把输入改为 1000 时, “exp“ 的消耗变为 1293 而 “mul” 的消耗仍然为 1084
这是为啥呢?
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)
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!