现在写合约中有很多方法可以节省 Gas, 这里发现一个不错案例比较循序渐进,可以参考。
现在写合约中有很多方法可以节省 Gas, 这里发现一个不错案例比较循序渐进,可以参考。
一个计算输入数组中偶数之和的方法,并记录计算之后的结果。
输入恒定 [12, 3, 4, 5, 3, 44, 2, 12, 3, 4, 5, 21, 46, 1, 2, 12]
uint public total;
function sumIfEvenAndLessThan99(uint[] memory nums) external {
for (uint i = 0; i < nums.length; i += 1) {
bool isEven = nums[i] % 2 == 0;
bool isLessThan99 = nums[i] < 99;
if (isEven && isLessThan99) {
total += nums[i];
}
}
}
Solidity 变量中 memory 、calldata 2 个表示作用非常类似,都是函数内部临时变量,它们最大的区别就是 calldata 是不可修改的,在某些只读的情况比较省 Gas.
参数从 memory 改为 calldata - 51858
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
for (uint i = 0; i < nums.length; i += 1) {
bool isEven = nums[i] % 2 == 0;
bool isLessThan99 = nums[i] < 99;
if (isEven && isLessThan99) {
total += nums[i];
}
}
}
Solidity 函数也是类似栈到结构,对函数内部变量的读写要比读写外部变量省 Gas,在一些需要高频读写的场景将函数外部变量拷贝到函数内部操作是一个不错的方法。
循环内高频写入的状态变量拷贝到内存中 - 51428
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
for (uint i = 0; i < nums.length; i += 1) {
bool isEven = nums[i] % 2 == 0;
bool isLessThan99 = nums[i] < 99;
if (isEven && isLessThan99) {
_total += nums[i];
}
}
total = _total;
}
Solidity 声明的内存是要算 Gas 的,某些时候可以适当减少内部声明变量。
循环内部没错都会声明isEven
、isLessThan99
2 个变量,这歌变量只是用来做一次条件判断,明显是可以合并起来,减少内部声明的变量。
合并判断条件,减少内部变量 - 50870
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
for (uint i = 0; i < nums.length; i += 1) {
if (nums[i] % 2 == 0 && nums[i] < 99) {
_total += nums[i];
}
}
total = _total;
}
这个似乎和 c++ 语言特性有关
循环自增变量优化 - 50258
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
for (uint i = 0; i < nums.length; ++i) {
if (nums[i] % 2 == 0 && nums[i] < 99) {
_total += nums[i];
}
}
total = _total;
}
数组长度nums.length
与数组循环变量 nums[i]
每次循环都会读,而且是从参数中读,可以拷贝到内存当中更加节约 Gas。
将数组元素加载到内存 - 50028
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
uint len = nums.length;
for (uint i = 0; i < len; ++i) {
uint num = nums[i];
if (num % 2 == 0 && num < 99) {
_total += num;
}
}
total = _total;
}
统计了下每个修改之后和最开始相比节省的 Gas。
操作 | Gas | 节约 Gas |
---|---|---|
初始 | 63964 | 0 |
参数从 memory 改为 calldata | 59897 | 4067 |
循环内高频写入的状态变量拷贝到内存中 | 58219 | 5745 |
合并判断条件,减少内部变量 | 57358 | 6606 |
循环自增变量优化 | 56315 | 7649 |
将数组元素加载到内存 | 55636 | 8328 |
将数长度加载到内存 | 55543 | 8421 |
计算 Gas 其实只有 2 个输入参数 时间
、空间
, 写合约的时候也只是对这 2 个输入做权衡、取舍。最近 10 年云计算的迅猛发展,虽然单一硬件性能已经挤不动牙膏了,但是可以大规模堆计算资源,所以并未如此细致的计算时间,空间。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!