本篇内容主要讲解“节约Gas成本的Solidity代码模式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“节约Gas成本的Solidity代码模式有哪些”吧!
在以太坊区块链上,Gas被用来补偿矿工为智能合约的存储与执行所提供的算力。目前以太坊的利用在逐渐增长,而交易手续费成本也水涨传告 —— 现在每天的gas成本已经高达数百万美元。随着以太坊生态系统的扩大,Solidity智能合约开发者也需要关注gas利用的优化问题了。
短路(short-circuiting)是一种使用或/与逻辑来排序不同成本操作的solidity合约开发模式,它将低gas成本的操作放在前面,高gas成本的操作放在后面,这样如果前面的低成本操作可行,就可以跳过(短路)后面的高成本以太坊虚拟机操作了。
// f(x) 是低gas成本的操作 // g(y) 是高gas成本的操作 // 按如下排序不同gas成本的操作 f(x) || g(y) f(x) && g(y)
在开发Solidity智能合约时,我们引入的库通常只需要用到其中的部分功能,这意味着其中可能会包含大量对于你的智能合约而言其实是冗余的solidity代码。如果可以在你自己的合约里安全有效地实现所依赖的库功能,那么就能够达到优化solidity合约的gas利用的目的。
例如,在下面的solidity代码中,我们的以太坊合约只是用到了SafeMath库的add
方法:
import './SafeMath.sol' as SafeMath; contract SafeAddition { function safeAdd(uint a, uint b) public pure returns(uint) { return SafeMath.add(a, b); } }
通过参考SafeMath的这部分代码的实现,可以把对这个solidity库的依赖剔除掉:
contract SafeAddition { function safeAdd(uint a, uint b) public pure returns(uint) { uint c = a + b; require(c >= a, "Addition overflow"); return c; } }
在Solidity合约开发种,显式声明函数的可见性不仅可以提高智能合约的安全性,同时也有利于优化合约执行的gas成本。例如,通过显式地标记函数为外部函数(External),可以强制将函数参数的存储位置设置为calldata
,这会节约每次函数执行时所需的以太坊gas成本。
在Solidity中,有些数据类型要比另外一些数据类型的gas成本高。有必要了解可用数据类型的gas利用情况,以便根据你的需求选择效率最高的那种。下面是关于solidity数据类型gas消耗情况的一些规则:
在任何可以使用uint
类型的情况下,不要使用string
类型
存储uint256要比存储uint8的gas成本低,为什么?点击这里查看原文
当可以使用bytes
类型时,不要在solidity合约种使用byte[]
类型
如果bytes
的长度有可以预计的上限,那么尽可能改用bytes1~bytes32这些具有固定长度的solidity类型
bytes32所需的gas成本要低于string类型
死代码(Dead code)是指那些永远也不会执行的Solidity代码,例如那些执行条件永远也不可能满足的代码,就像下面的两个自相矛盾的条件判断里的Solidity代码块,消耗了以太坊gas资源但没有任何作用:
function deadCode(uint x) public pure { if(x < 1) { if(x > 2) { return x; } } }
有些条件断言的结果不需要Solidity代码的执行就可以了解,那么这样的条件判断就可以精简掉。例如下面的Solidity合约代码中的两级判断条件,最外层的判断是在浪费宝贵的以太坊gas资源:
function opaquePredicate(uint x) public pure { if(x < 1) { if(x < 0) { return x; } } }
由于SLOAD
和SSTORE
操作码的成本高昂,因此管理storage变量的gas成本要远远高于内存变量,所以要避免在循环中操作storage变量。例如下面的solidity代码中,num
变量是一个storage变量,那么未知循环次数的若干次操作,很可能会造成solidity开发者意料之外的以太坊gas消耗黑洞:
uint num = 0; function expensiveLoop(uint x) public { for(uint i = 0; i < x; i++) { num += 1; } }
解决上述反模式以太坊合约代码的方法,是创建一个solidity临时变量来代替上述全局变量参与循环,然后在循环结束后重新将临时变量的值赋给全局变量:
uint num = 0; function lessExpensiveLoop(uint x) public { uint temp = num; for(uint i = 0; i < x; i++) { temp += 1; } num = temp; }
如果一个循环计算的结果是无需编译执行Solidity代码就可以预测的,那么就不要使用循环,这可以可观地节省gas。例如下面的以太坊合约代码就可以直接设置num变量的值:
function constantOutcome() public pure returns(uint) { uint num = 0; for(uint i = 0; i < 100; i++) { num += 1; } return num; }
有时候在Solidity智能合约中,你会发现两个循环的判断条件一致,那么在这种情况下就没有理由不合并它们。例如下面的以太坊合约代码:
function loopFusion(uint x, uint y) public pure returns(uint) { for(uint i = 0; i < 100; i++) { x += 1; } for(uint i = 0; i < 100; i++) { y += 1; } return x + y; }
如果循环中的某个Solidity表达式在每次迭代都产生同样的结果,那么就可以将其移出循环先行计算,从而节省掉循环中额外的gas成本。如果表达式中使用的变量是storage变量,这就更重要了。例如下面的智能合约代码中表达式a*b
的值,并不需要每次迭代重新计算:
uint a = 4; uint b = 5; function repeatedComputations(uint x) public returns(uint) { uint sum = 0; for(uint i = 0; i <= x; i++) { sum = sum + a * b; } }
到此,相信大家对“节约Gas成本的Solidity代码模式有哪些”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。