优化 Solidity 中的气体
在本教程中,我们将学习 Solidity 中的一些气体优化技术。这是我们要求最多的一个级别,所以让我们开始吧,不要再多说了。
技巧和窍门
可变包装
如果你还记得我们在之前的一个关卡中谈到了存储槽。现在,如果你记得的话,solidity 中有趣的一点是,每个存储槽是 32 字节。
如果你正确地打包你的变量,这个存储槽可以被优化,这将进一步意味着你在部署你的智能合约时的气体优化。
打包你的变量意味着你将较小尺寸的变量打包或放在一起,使它们共同形成 32 个字节
。例如,你可以将 32 个 uint8
打包到一个存储槽中,但要做到这一点,你必须连续声明它们,因为变量的声明顺序在 solidity 中很重要。
给出两个代码样本。
uint8 num1;
uint256 num2;
uint8 num3;
uint8 num4;
uint8 num5。
uint8 num1;
uint8 num3;
uint8 num4;
uint8 num5。
uint256 num2。
第二种情况更好,因为在第二种情况下,solidity 编译器会把所有的 uint8 放在一个存储槽中,但在第一种情况下,它会把 uint8 num1 放在一个槽中,但现在它看到的下一个是 uint256,它本身需要 32 字节,因为 256/8 比特=32 字节,所以它不能和 uint8 num1 放在同一个存储槽中,所以现在它将需要另一个存储槽。之后,uint8 num3, num4, num5 将被放在另一个存储槽中。因此,第二个例子需要 2 个存储槽,而第一个例子则需要 3 个存储槽。
还需要注意的是,内存中的元素和 calldata 不能被打包,也不能被 solidity 的编译器优化。
存储与内存
改变存储变量比改变内存中的变量需要更多的气体。最好是在所有的逻辑都已经实现之后,在最后更新存储变量。
因此,给定两个代码的样本
contract A {
uint public counter = 0;
function count() {
for(uint i = 0; i < 10; i++) {
counter++;
}
}
}
contract B {
uint public counter = 0;
function count() {
uint copyCounter;
for(uint i = 0; i < 10; i++) {
copyCounter++;
}
counter = copyCounter;
}
}
第二段代码样本更加气体优化,因为我们只向存储变量 counter 写了一次,而第一段代码样本是在每个迭代中向存储写。尽管我们在第二个代码样本中多写了一次,但向内存写 10 次,向存储写 1 次,仍然比直接向存储写 10 次便宜。