案例 一个计算输入数组中偶数之和的方法,并记录计算之后的结果。 输入恒定 [12, 3, 4, 5, 3, 44, 2, 12, 3, 4, 5, 21, 46, 1, 2, 12] 初始 Code 未优化uint public total;function sumIfEvenAndLessThan99(uint[] memory nums) external { for (uint i = 0; i < nums.length; i += 1) { bool isEven = nums % 2 == 0; bool isLessThan99 = nums < 99; if (isEven && isLessThan99) { total += nums; } }}只读参数使用 calldataSolidity 变量中 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 % 2 == 0; bool isLessThan99 = nums < 99; if (isEven && isLessThan99) { total += nums; } }}高频读写参数拷贝到函数内部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 % 2 == 0; bool isLessThan99 = nums < 99; if (isEven && isLessThan99) { _total += nums; } } total = _total;}减少变量声明次数Solidity 声明的内存是要算 Gas 的,某些时候可以适当减少内部声明变量。
循环内部没错都会声明isEven、isLessThan992 个变量,这歌变量只是用来做一次条件判断,明显是可以合并起来,减少内部声明的变量。 合并判断条件,减少内部变量 - 50870 uint public total;function sumIfEvenAndLessThan99(uint[] calldata nums) external { uint _total = total; for (uint i = 0; i < nums.length; i += 1) { if (nums % 2 == 0 && nums < 99) { _total += nums; } } 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 % 2 == 0 && nums < 99) { _total += nums; } } total = _total;}将高频读取的参数拷贝的函数内部数组长度nums.length 与数组循环变量 nums每次循环都会读,而且是从参数中读,可以拷贝到内存当中更加节约 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; 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 年云计算的迅猛发展,虽然单一硬件性能已经挤不动牙膏了,但是可以大规模堆计算资源,所以并未如此细致的计算时间,空间。
|