Gas - 驅(qū)動以太坊DApps的能源
在 Solidity 中似忧,你的用戶想要每次執(zhí)行你的 DApp 都需要支付一定的 gas,gas 可以用以太幣購買丈秩,因此盯捌,用戶每次跑 DApp 都得花費以太幣。
一個 DApp 收取多少 gas 取決于功能邏輯的復雜程度蘑秽。每個操作背后饺著,都在計算完成這個操作所需要的計算資源,(比如肠牲,存儲數(shù)據(jù)就比做個加法運算貴得多)幼衰, 一次操作所需要花費的 gas 等于這個操作背后的所有運算花銷的總和。
由于運行你的程序需要花費用戶的真金白銀缀雳,在以太坊中代碼的編程語言渡嚣,比其他任何編程語言都更強調(diào)優(yōu)化。同樣的功能,使用笨拙的代碼開發(fā)的程序严拒,比起經(jīng)過精巧優(yōu)化的代碼來扬绪,運行花費更高,這顯然會給成千上萬的用戶帶來大量不必要的開銷裤唠。
省 gas 的招數(shù):
1、結(jié)構(gòu)封裝 (Struct packing)
在第1課中莹痢,我們提到除了基本版的 uint 外种蘸,還有其他變種 uint:uint8,uint16竞膳,uint32等航瞭。
通常情況下我們不會考慮使用 uint 變種,因為無論如何定義 uint的大小坦辟,Solidity 為它保留256位的存儲空間刊侯。例如,使用 uint8 而不是uint(uint256)不會為你節(jié)省任何 gas锉走。
除非滨彻,把 uint 綁定到 struct 里面。
如果一個 struct 中有多個 uint挪蹭,則盡可能使用較小的 uint, Solidity 會將這些 uint 打包在一起亭饵,從而占用較少的存儲空間。例如:
struct NormalStruct {
uint a;
uint b;
uint c;
}
struct MiniMe {
uint32 a;
uint32 b;
uint c;
}
// 因為使用了結(jié)構(gòu)打包梁厉,mini
比 normal
占用的空間更少
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);
所以辜羊,當 uint 定義在一個 struct 中的時候,盡量使用最小的整數(shù)子類型以節(jié)約空間词顾。 并且把同樣類型的變量放一起(即在 struct 中將把變量按照類型依次放置)八秃,這樣 Solidity 可以將存儲空間最小化。例如肉盹,有兩個 struct:
uint c; uint32 a; uint32 b;
和
uint32 a; uint c; uint32 b;
前者比后者需要的gas更少昔驱,因為前者把uint32放一起了。
2垮媒、存儲非常昂貴
Solidity 使用storage(存儲)是相當昂貴的舍悯,”寫入“操作尤其貴。
這是因為睡雇,無論是寫入還是更改一段數(shù)據(jù)萌衬, 這都將永久性地寫入?yún)^(qū)塊鏈∷В”永久性“帮踉ァ!需要在全球數(shù)千個節(jié)點的硬盤上存入這些數(shù)據(jù),隨著區(qū)塊鏈的增長混移,拷貝份數(shù)更多祠墅,存儲量也就越大。這是需要成本的歌径!
為了降低成本毁嗦,不到萬不得已,避免將數(shù)據(jù)寫入存儲回铛。這也會導致效率低下的編程邏輯 - 比如每次調(diào)用一個函數(shù)狗准,都需要在 memory(內(nèi)存) 中重建一個數(shù)組,而不是簡單地將上次計算的數(shù)組給存儲下來以便快速查找茵肃。
在大多數(shù)編程語言中腔长,遍歷大數(shù)據(jù)集合都是昂貴的。但是在 Solidity 中验残,使用一個標記了external view的函數(shù)捞附,遍歷比 storage 要便宜太多,因為 view 函數(shù)不會產(chǎn)生任何花銷您没。 (gas可是真金白銀澳裾佟!)紊婉。
我們將在下一章討論for循環(huán)药版,現(xiàn)在我們來看一下看如何如何在內(nèi)存中聲明數(shù)組。
在內(nèi)存中聲明數(shù)組
在數(shù)組后面加上 memory關鍵字喻犁, 表明這個數(shù)組是僅僅在內(nèi)存中創(chuàng)建槽片,不需要寫入外部存儲,并且在函數(shù)調(diào)用結(jié)束時它就解散了肢础。與在程序結(jié)束時把數(shù)據(jù)保存進 storage 的做法相比还栓,內(nèi)存運算可以大大節(jié)省gas開銷 -- 把這數(shù)組放在view里用,完全不用花錢传轰。
以下是申明一個內(nèi)存數(shù)組的例子:
function getArray() external pure returns(uint[]) {
// 初始化一個長度為3的內(nèi)存數(shù)組
uint[] memory values = new uint[](3);
// 賦值
values.push(1);
values.push(2);
values.push(3);
// 返回數(shù)組
return values;
}
注意:內(nèi)存數(shù)組 必須 用長度參數(shù)(在本例中為3)創(chuàng)建剩盒。目前不支持 array.push()之類的方法調(diào)整數(shù)組大小,在未來的版本可能會支持長度修改慨蛙。