中华街zhshops

浅谈 Solidity Gas 优化

案例
一个计算输入数组中偶数之和的方法,并记录计算之后的结果。
输入恒定 [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;        }    }}只读参数使用 calldata
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 % 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 年云计算的迅猛发展,虽然单一硬件性能已经挤不动牙膏了,但是可以大规模堆计算资源,所以并未如此细致的计算时间,空间。

分享至 : QQ空间
收藏

0 个回复

您需要登录后才可以回帖 登录 | 立即注册