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 如何分析
兩個寫法最終的差別體現(xiàn)在是用運(yùn)算符 " * " 還是 " ** " 上。
運(yùn)算符 " * " 最終會用到 EVM 指令 MUL
運(yùn)算符 " ** " 最終會用到 EVM 指令 EXP
對于 MUL 和 EXP 兩個指令的消耗,我們可以查表:https://ethereum.org/en/developers/docs/evm/opcodes/
通過查表得知,MUL 指令一次消耗的 gas 是 5,兩次 MUL 消耗的 gas 就是 10
EXP 指令的消耗有個動態(tài)計(jì)算公式
gas_cost = 10 + 50 * byte_len_exponent
" x ** 3 " 所用到 EXP 指令的消耗就是 10 + 50 * 1 = 60
但是不是這就意味著 "mul" 這個函數(shù)比 "exp" 這個函數(shù)更省 gas 呢?
0x03 實(shí)證
把代碼在 remix 運(yùn)行,當(dāng)輸入為 100 時,“exp” 的消耗是 1052
“mul” 的消耗是 1084
“exp” 函數(shù)比 "mul" 更省 gas,為啥呢?
這是因?yàn)?“ x * x * x" 這種寫法需要更多的中間指令,如果對比 "x * x" 和 "x ** 2" 就會發(fā)現(xiàn) "x * x" 要更省 gas 了。
但是,當(dāng)我們把輸入改為 1000 時,“exp“ 的消耗變?yōu)?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.
明白了么?如果基數(shù)大小超過 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)
}
}