本篇內容主要講解“節約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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。