1,摘要
在以太坊上骗灶,代碼即法律惨恭,交易即金錢。每一筆智能合約的運行耙旦,都要根據(jù)復(fù)雜度消耗一筆GAS費(ETH)脱羡。那么,智能合約solidity語言的編寫母廷,不僅要考慮安全轻黑,也要考慮語言的優(yōu)化,以便高效便宜了琴昆。
本文將從以下一些方面分析如何節(jié)約GAS的編程總結(jié):
1)如何在REMIX編譯器上分析GAS/GAS LIMIT等信息
2) 如何優(yōu)化節(jié)省GAS費用的方法
- 創(chuàng)建合約優(yōu)化
- 存儲優(yōu)化
- 變量排序優(yōu)化
- 交易輸入數(shù)據(jù)優(yōu)化
- 轉(zhuǎn)賬優(yōu)化
- 部署合約優(yōu)化
- 調(diào)用合約函數(shù)的成本優(yōu)化
2氓鄙,如何在REMIX編譯器上分析GAS/GAS LIMIT等信息
如果你想了解以太坊的賬戶、交易业舍、Gas和Gas Limit等基本概念信息抖拦,可以閱讀文章《以太坊的賬戶、交易舷暮、Gas和Gas Limit》态罪。
如果你不了解以太坊智能合約語言solidity編譯IDE環(huán)境REMIX,可以閱讀文章《第十課 Solidity語言編輯器REMIX指導(dǎo)大全》。
本章節(jié)聚焦在如何通過REMIX編譯器查看GAS/GAS LIMIT等信息下面。
2.1 簡單智能合約樣例
以太坊指令執(zhí)行主要依靠GAS复颈。當你執(zhí)行智能合約時,它會消耗GAS沥割。所以耗啦,如果你正在運行一個智能合約,那么每一條指令都要花費一定數(shù)量的GAS費机杜。這有兩個因素帜讲,即您發(fā)送的GAS數(shù)量和總區(qū)塊GAS上限(a total block gas limit)。
舉例來說椒拗,一個簡單的智能合約似将,有一個保存無符號整數(shù)256值的函數(shù)。
合約代碼如下:
pragma solidity ^0.4.19;
contract A {
uint b;
function saveB(uint _b) public {
b = _b;
}
}
如果你將此合約復(fù)制并粘貼到Remix中蚀苛,則可以運行此合約在验。通過MIST或來自網(wǎng)站的MetaMask與此合同進行交互的方式類似。
讓我們運行saveB(5)并查看日志窗口中發(fā)生的情況:
這兒有3個我們感興趣的值:
- GAS總量( "gas limit"): 3,000,000
- 交易費用 ("transaction cost"): 41642 gas
- 執(zhí)行費用( "execution cost"): 20178 gas.
2.2 發(fā)送的GAS總量(Gas limit)
這兒顯示的"Gas limit"是發(fā)送的GAS總量堵未,Value是發(fā)給目標地址的ETH值译红。這2處的值可以被發(fā)送交易的用戶修改。
2.3 交易成本(Transaction Cost)
交易成本兴溜,在Remix中顯示侦厚,是實際交易成本加上執(zhí)行成本的混合耻陕。我認為,這兒看起來有點誤導(dǎo)刨沦。
如果您使用數(shù)據(jù)字段發(fā)送交易诗宣,那么交易包含一個基本成本和每個字節(jié)的附加成本(GAS計價)∠胱纾看看以太坊黃紙的附錄列出了每種的GAS費用:
一起來看看41642的交易成本是如何結(jié)合在一起的召庞。這是Remix在交易中自動發(fā)送的數(shù)據(jù)字段:
這兒是 Data-Field:
0x348218ec0000000000000000000000000000000000000000000000000000000000000005
數(shù)據(jù)字段是散列函數(shù)簽名的前4個字節(jié)和32字節(jié)填充參數(shù)的組合。我們快速手動計算来破。
函數(shù)簽名是saveB(uint256)篮灼,如果我們用SHA3-256(或Keccak-256)散列函數(shù),那么我們得到:348218ec5e13d72ab0b6b9db1556cba7b0b97f5626b126d748db81c97e97e43d
如果我們?nèi)∏?個字節(jié)(提醒:1個字節(jié)= 8位= 2個十六進制字符.1個十六進制字符= 4 bit = 0-15 = 0000到1111 = 0x0到0xF)徘禁,然后我們得到348218ec诅诱。讓我們0x在前面添加,我們得到0x348218ec送朱。參數(shù)是一個256位的無符號整數(shù)娘荡,即32個字節(jié)。這意味著它將整數(shù)“5”填充到32個字節(jié)驶沼,換句話說炮沐,它將在數(shù)字前面添加63個零:
0000000000000000000000000000000000000000000000000000000000000005
。
從以太坊黃皮書上可以獲得參考:
- 每筆交易都有21000 GAS支付
- 為交易的每個非零字節(jié)數(shù)據(jù)或代碼支付68 GAS
- 為交易的每個零字節(jié)數(shù)據(jù)或代碼支付4 GAS
計算一下:
348218ec 是4個字節(jié)的數(shù)據(jù)回怜,顯然是非零的大年。
0000000000000000000000000000000000000000000000000000000000000005
是31個字節(jié)的零數(shù)據(jù)和1個字節(jié)的非零數(shù)據(jù)的混合。
這使得總共5個字節(jié)的非零數(shù)據(jù)和31個字節(jié)的零數(shù)據(jù)玉雾。
(5 non-zero-bytes * 68 gas) + (31 zero-bytes * 4 gas) = 340 + 124 = 464 gas
對于我們的輸入數(shù)據(jù)鲜戒,我們必須支付464 GAS。除此之外抹凳,我們還要支付 21000 GAS,這是每筆交易支付的伦腐。因此總共需要21464用于交易赢底。
讓我們看看是否會增加。
Remix稱“交易成本”為41642 gas柏蘑,“執(zhí)行成本”為 20178 gas幸冻。而在Remix中,“交易成本”實際上是交易成本加執(zhí)行成本的總和咳焚。因此洽损,如果我們從交易成本中減去執(zhí)行成本,我們應(yīng)該得到21464 gas革半。
41642 (交易成本”) - 20178 (執(zhí)行成本) = 21464 gas
剩下的結(jié)果21464 gas為數(shù)據(jù)交易成本碑定,同上計算公式流码。
2.4 執(zhí)行成本(Execution Cost)
執(zhí)行成本有點難以計算,因為發(fā)生了很多事情延刘,輝哥試著告訴你合同執(zhí)行時到底發(fā)生了什么漫试。
讓我們深入了解實際的事務(wù)并打開調(diào)試器。這可以通過單擊事務(wù)旁邊的“調(diào)試”按鈕來完成碘赖。
可以打開指令折疊菜單和單步調(diào)試菜單驾荣。你將看到每一條指令以及每個指令在該特定步驟中花費的GAS費用。
這里看到的是所有以太坊匯編指令普泡。因此播掷,我們知道Solidity可以歸結(jié)為EVM Assembly。這是礦工實際執(zhí)行的智能合約運行看起來的實際情況撼班。來看看前兩個指令:
PUSH1 60
PUSH1 40
這意味著除了將值60和40推入堆棧之外別無其他歧匈。顯然還有很多事情要做,你可以通過在單步調(diào)試器中移動藍色滑塊來完成它們的工作权烧。
根據(jù)以太坊黃皮書將每個指令所需的確切氣體量匯總在一起眯亦,以便將值5寫入存儲:
GAS Instruction
3 000 PUSH1 60
3 002 PUSH1 40
12 004 MSTORE
3 005 PUSH1 04
2 007 CALLDATASIZE
3 008 LT
3 009 PUSH1 3f
10 011 JUMPI
3 012 PUSH1 00
3 014 CALLDATALOAD
3 015 PUSH29 0100000000000000000000000000000000000000000000000000000000
3 045 SWAP1
5 046 DIV
3 047 PUSH4 ffffffff
3 052 AND
3 053 DUP1
3 054 PUSH4 348218ec
3 059 EQ
3 060 PUSH1 44
10 062 JUMPI
1 068 JUMPDEST
2 069 CALLVALUE
3 070 ISZERO
3 071 PUSH1 4e
10 073 JUMPI
3 074 PUSH1 00
3 076 DUP1
1 078 JUMPDEST
3 079 PUSH1 62
3 081 PUSH1 04
3 083 DUP1
3 084 DUP1
3 085 CALLDATALOAD
3 086 SWAP1
3 087 PUSH1 20
3 089 ADD
3 090 SWAP1
3 091 SWAP2
3 092 SWAP1
2 093 POP
2 094 POP
3 095 PUSH1 64
8 097 JUMP
1 100 JUMPDEST
3 101 DUP1
3 102 PUSH1 00
3 104 DUP2
3 105 SWAP1
20000 106 SSTORE
2 107 POP
2 108 POP
8 109 JUMP
1 098 JUMPDEST
0 099 STOP
合計為20178 GAS費。
2.5 GAS上限(Gas Limit)
所以般码,以太坊區(qū)塊鏈上的每一條指令都會消耗一些GAS妻率。如果你要將值寫入存儲,則需要花費很多板祝。如果你只是使用堆棧宫静,它的成本會低一些。但基本上所有關(guān)于EVM的指令都需要GAS券时。這意味著智能合約只能做有限的事情孤里,直到發(fā)送的GAS用完為止。在樣例這種情況下橘洞,我們發(fā)送了300萬 GAS費捌袜。
當您返回REMIX的單步調(diào)試器,點擊第一步時炸枣,您會看到每個步驟剩余多少GAS虏等。輝哥在第一步打開它:
它已經(jīng)從我們發(fā)送的300萬(從3,000,000 - 21464 = 2,978,536)中扣除的交易成本開始。(說明:21464是之前2.3章節(jié)執(zhí)行的數(shù)據(jù)執(zhí)行成本适肠。)
一旦此計數(shù)器達到零霍衫,那么合約執(zhí)行將立即停止,所有存儲的值將被回滾侯养,你將獲得“Out of Gas”異常告警敦跌。
2.6 區(qū)塊GAS上限(Block Gas Limit)
除了通過交易設(shè)置的氣Gas Limit外,還有一個所謂的“區(qū)塊上限”逛揩。這是你可以發(fā)送的最大GAS量柠傍。目前麸俘,在Main-Net,該值大概為8M左右携兵。
2.7 GAS退款(Gas Refund)
Gas Limit有一個好處:你不必自己計算它疾掰。如果你向合約發(fā)送8M的GAS,它耗盡41642 GAS徐紧,可以退還其余部分静檬。因此,發(fā)送遠遠超過必要的GAS總會節(jié)省下來的并级,其余的將自動退還到你的賬號地址拂檩。
2.8 GAS價格(Gas Price)
GAS價格決定了交易在能否被包含在下一個被挖出的區(qū)塊中。
當你發(fā)送交易時嘲碧,你可以激勵礦工接下來處理您的交易稻励。這種激勵就是GAS PRICE。礦工一旦挖出新區(qū)塊愈涩,也會將交易納入該區(qū)塊望抽。哪些交易被納入下一個區(qū)塊是由礦工確定的 - 但他很可能將GAS PRICE從高到低排序。
假設(shè)有15筆未完成的交易履婉,但只有12筆交易可以進入下一個區(qū)塊煤篙。5個20 Gwei,5個15 Gwei和5個 5Gwei的GAS PRICE毁腿。礦工很可能按此順序選擇交易:5 * 20 + 5 * 15 + 2 * 5 Gwei并將它們合并到下一個挖掘區(qū)塊中辑奈。
因此,GAS Limit基本上決定了以太坊虛擬機可以執(zhí)行的指令數(shù)量已烤,而GAS Price決定了礦工選擇此交易的可能性鸠窗。
大多數(shù)錢包將標準GAS Price設(shè)定為20Gwei左右(0.00000002 ETH)。如果您正在執(zhí)行上述合約胯究,那么您將支付約60-70美分(美元分)稍计,當前匯率為1 ETH = 800美元。所以它根本不便宜裕循。
幸運的是臣嚣,在網(wǎng)絡(luò)擁塞期間,您只需要更高的GAS PRICE费韭,那是因為許多人嘗試同時發(fā)送交易。如果網(wǎng)絡(luò)沒有擁擠庭瑰,那么您不需要支付這么多GAS星持。EthGasStation網(wǎng)站評估目前的交易價格為4 Gwei足夠 。所以弹灭,憑借這個小功能督暂,只需要4 Gwei的GAS揪垄,它將是16美分左右,而不是65美分逻翁。一個巨大的差異饥努。
3,如何優(yōu)化節(jié)省GAS費用的方法
GAS消耗可參考以下兩個表: 表格1 和 表格2 八回。下面提供一下優(yōu)化GAS消耗的方法酷愧。
3.1 創(chuàng)建合約
創(chuàng)建合約對應(yīng)CREATE和CODECOPY這兩條指令。在合約中創(chuàng)建另一個空合約消耗42,901個GAS(總共64,173個GAS)缠诅。如果直接部署空白合約溶浴,共有68,653個GAS。
如果包含實施管引,可能會有數(shù)十萬甚至數(shù)百萬的GAS士败。它應(yīng)該是所有指令中最昂貴的。如果創(chuàng)建多個合約實例褥伴,則GAS消耗可能很大谅将。
建議: 避免將合約用作數(shù)據(jù)存儲。
不好的代碼實現(xiàn):
contract User {
uint256 public amount;
bool public isAdmin;
function User(uint256 _amount, bool _isAdmin) {
amount = _amount;
isAdmin = _isAdmin;
}
}
好的代碼實現(xiàn):
contract MyContract {
mapping(address => uint256) amount;
mapping(address => bool) isAdmin;
}
另一種OK的代碼實現(xiàn):
contract MyContract {
struct {
uint256 amount;
bool isAdmin;
}
mapping(address => User) users;
}
3.2 存儲
對應(yīng)于SSTORE指令重慢。存儲新數(shù)據(jù)需要20,000 GAS饥臂。修改數(shù)據(jù)需要5000 GAS。一個例外是將非零變量更改為零伤锚。我們稍后會討論這個問題擅笔。
建議: 避免重復(fù)寫入,最好一次在最后盡可能多地寫入到存儲變量屯援。
不好的代碼樣例:
uint256 public count;
// ...
for (uint256 i = 0; i < 10; ++i) {
// ...
++count;
}
好的代碼樣例:
for (uint256 i = 0; i < 10; ++i) {
// ...
}
count += 10;
3.3 變量排序?qū)AS的影響
你可能不知道變量聲明的順序也會影響Gas的消耗猛们。
由于EVM操作都是以32字節(jié)為單位執(zhí)行的,因此編譯器將嘗試將變量打包成32字節(jié)集進行訪問狞洋,以減少訪問時間弯淘。
但是,編譯器不夠智能吉懊,無法自動優(yōu)化變量分組庐橙。它將靜態(tài)大小的變量分組為32個字節(jié)的組。例如:
contract MyContract {
uint64 public a;
uint64 public b;
uint64 public c;
uint64 public d;
function test() {
a = 1;
b = 2;
c = 3;
d = 4;
}
}
執(zhí)行test()時借嗽,看起來已經(jīng)存儲了四個變量态鳖。由于這四個變量之和恰好是32個字節(jié),因此實際執(zhí)行了一個SSTORE恶导。這只需要20,000 GAS浆竭。
再看下一個例子:
contract MyContract {
uint64 public a;
uint64 public b;
byte e;
uint64 public c;
uint64 public d;
function test() {
a = 1;
b = 2;
c = 3;
d = 4;
}
}
中間插入了另一個變數(shù),結(jié)果造成a,b邦泄,e和c會被分為一組删窒,d獨立為一組。同樣的test()造成兩次寫入顺囊,消耗40000 Gas肌索。
最后再看一個例子:
contract MyContract {
uint64 public a;
uint64 public b;
uint64 public c;
uint64 public d;
function test() {
a = 1;
b = 2;
// ... do something
c = 3;
d = 4;
}
}
這與第一個例子的區(qū)別在于,在存儲a和b之后特碳,完成了其他事情诚亚,最后存儲了c和d。結(jié)果這次將導(dǎo)致兩次寫入测萎。因為當執(zhí)行“執(zhí)行某事”時亡电,編譯器確定打包操作已結(jié)束,然后發(fā)送寫入硅瞧。但是份乒,由于第二次寫入是同一組數(shù)據(jù),因此認為它是被修改的腕唧。將消耗總共25,000個氣體或辖。
建議:
根據(jù)上述原則,我們可以很容易地知道如何處理它枣接。
- 正確的排序和分組
將數(shù)據(jù)大小分組為32個字節(jié)颂暇,并將通常同時更新的變量放在一起。
不好的代碼例子:
contract MyContract {
uint128 public hp;
uint128 public maxHp;
uint32 level;
uint128 public mp;
uint128 public maxMp;
}
好的例子:
contract MyContract {
uint128 public hp;
uint128 public mp;
uint128 public maxHp;
uint128 public maxMp;
uint32 level;
}
這里我們假設(shè)hp和mp更頻繁地更新但惶,并且maxHp和maxMp更頻繁地一起更新耳鸯。
- 盡量一次訪問
不好的代碼例子:
function test() {
hp = 1;
// ... do something
mp = 2;
}
好的例子:
function test() {
// ... do something
hp = 1;
mp = 2;
}
這個規(guī)則在struct上是一樣的。
3.4 交易輸入數(shù)據(jù)
合約交易的基本氣體是21,000膀曾。輸入數(shù)據(jù)為每字節(jié)68個GAS县爬,如果字節(jié)為0x00則為4個GAS。
例如添谊,如果數(shù)據(jù)為0x0dbe671f财喳,則氣體為68 * 4 = 272; 如果是0x0000001f,它是68 * 1 + 4 * 3 = 80斩狱。
由于所有參數(shù)都是32字節(jié)耳高,因此當參數(shù)為零時,氣體消耗最小所踊。它將是32 * 4 = 128泌枪。最大值如下:
n * 68 +(32-n)* 4 的字節(jié)數(shù) (n:參數(shù))
例如,32字節(jié)輸入?yún)?shù)的最大GAS為2,176 (3268 = 2176)秕岛。輸入?yún)?shù)為地址碌燕,地址是20個字節(jié)乍赫,因此它是1,408 (2068+(32-20)*4 = 1408)。
建議: 可以通過更改排序來節(jié)省GAS消耗陆蟆。
例如EtherScan有下一段交易記錄:
Function: trade(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s, uint256 amount) ***
MethodID: 0x0a19b14a
[0]:0000000000000000000000000000000000000000000000000000000000000000
[1]:000000000000000000000000000000000000000000000000006a94d74f430000
[2]:000000000000000000000000a92f038e486768447291ec7277fff094421cbe1c
[3]:0000000000000000000000000000000000000000000000000000000005f5e100
[4]:000000000000000000000000000000000000000000000000000000000024cd39
[5]:00000000000000000000000000000000000000000000000000000000e053cefa
[6]:000000000000000000000000a11654ff00ed063c77ae35be6c1a95b91ad9586e
[7]:000000000000000000000000000000000000000000000000000000000000001c
[8]:caa3a70dd8ab2ea89736d7c12c6a8508f59b68590016ed99b40af0bcc2de8dee
[9]:26e2347abfba108444811ae5e6ead79c7bd0434cf680aa3102596f1ab855c571
[10]:000000000000000000000000000000000000000000000000000221b262dd8000
所有參數(shù)都是256位,無論類型是byte32惋增,address還是uint8叠殷。所以左邊的大多數(shù)參數(shù)都有大量的“0”是未使用的位。很容易想到使用這些“空間”诈皿。
例如可以把tokenGive的高位字節(jié)用于存放下面嗎一些變量林束,把命名改為uint256 tokenSellWithData。
nonce - > 40位
takerFee - > 16位
makerFee - > 16位
uint256 joyPrice - > 28位
isBuy - > 4位(實際上稽亏,1位就足夠了壶冒。只是為了方便呈現(xiàn)文檔)
假如上面變量的值分別為:
nonce: 0181bfeb
takerFee: 0014
makerFee: 000a
joyPrice: 0000000
isBuy: 1
那么tokenSellWithData的存儲可能如:
更多優(yōu)化參考文章《[Solidity] Compress input in smart contract》。
3.5 轉(zhuǎn)賬
Call, send 和transfer 函數(shù)對應(yīng)于CALL指令截歉∨痔冢基本消耗是7,400 GAS。事實上瘪松,消費將近7,600 GAS咸作。值得注意的是,如果轉(zhuǎn)賬到一個從未見過的地址宵睦,將額外增加25,000個GAS记罚。
沒有額外的消耗樣例:
function withdraw(uint256 amount){
msg.sender.transfer(amount);
}
可能會有額外的消耗樣例(receiver參數(shù)未被使用,多余參數(shù)):
function withdrawTo(uint256 amount, address receiver) {
receiver.transfer(amount);
}
3.6 其他命令
3.6.1 ecrecover
對應(yīng)CALL指令壳嚎。此功能將消耗3700 GAS桐智。
3.6.2調(diào)用外部合約
調(diào)用外部合約執(zhí)行EXTCODESIZE和CALL指令⊙滔冢基本消耗1400 GAS说庭。除非必要,否則不建議拆分多個合同焙糟】谟妫可以使用多個繼承來管理代碼。
3.6.3事件
對應(yīng)于LOG1指令穿撮。沒有參數(shù)的事件是750 GAS缺脉。理論上每個附加參數(shù)將增加256個GAS,但事實上悦穿,它會更多攻礼。
3.6.3哈希
你可以使用智能合約中的幾個內(nèi)置哈希函數(shù):keccak256,sha256和ripemd160栗柒。參數(shù)越多礁扮,消耗的氣體越多知举。耗氣量:ripemd160> sha256> keccak256。因此太伊,如果沒有其他目的雇锡,建議使用keccak256函數(shù)。
3.7 部署合約優(yōu)化
大部分的優(yōu)化在編譯時候已經(jīng)完成了僚焦。
問題:
部署合同中是否包含注釋锰提,是否會增加部署氣體?
回答:
不芳悲,在編譯期間刪除了執(zhí)行時不需要的所有內(nèi)容立肘。其中包括注釋,變量名和類型名稱名扛。
并且可以在此處文章找到優(yōu)化程序的詳細信息谅年。
另一種通過刪除無用代碼來減小大小的方法,肮韧。例如:
1 function p1 ( uint x ){
2 if ( x > 5)
3 if ( x*x < 20)
4 XXX }
在上面的代碼中融蹂,第3行和第4行永遠不會執(zhí)行,并且可以避免這些類型的無用代碼仔細通過合同邏輯弄企,這將減少智能合約的大小殿较。
3.8 調(diào)用合約函數(shù)的成本優(yōu)化
當調(diào)用合約額的功能時,為了執(zhí)行功能桩蓉,它需要GAS淋纲。因此,優(yōu)化使用較少GAS的功能非常重要院究。在考慮每個合約時時洽瞬,可以采用多種不同的方式。這里有一些可能在執(zhí)行過程中節(jié)省GAS的方法业汰。
3.8.1 減少昂貴的操作
昂貴的操作是指一些需要更多GAS值的操作碼伙窃,例如SSTORE
。以下是一些減少昂貴操作的方法样漆。
A)使用短路規(guī)則
操作符 || 和&&適用常見的短路規(guī)則为障。這意味著在表達式f(x)|| g(y)中,如果f(x)的計算結(jié)果為真放祟,即使它有副作用鳍怨,也不會評估g(y)。
因此跪妥,如果邏輯操作包括昂貴的操作和低成本操作鞋喇,那么以昂貴的操作可以短路的方式安排將在一些執(zhí)行中減少GAS。
如果f(x)是便宜的并且g(y)是昂貴的眉撵,邏輯運算代碼(便宜的放在前面):
- OR :
f(x) || g(y)
- AND:
f(x) && g(y)
如果短路侦香,將節(jié)省更多的氣體落塑。
f(x)
與g(y)
安排AND操作相比,如果返回錯誤的概率要高得多罐韩,f(x) && g(y)
可能會導(dǎo)致通過短路節(jié)省更多的氣體憾赁。
f(x)
與g(y)
安排OR運算相比,如果返回真值的概率要高得多散吵,f(x) || g(y)
可能會導(dǎo)致通過短路節(jié)省更多氣體缠沈。
B)循環(huán)中昂貴的操作
不好的代碼,例如:
uint sum = 0;
function p3 ( uint x ){
for ( uint i = 0 ; i < x ; i++)
sum += i; }
在上面的代碼中错蝴,由于sum
每次在循環(huán)內(nèi)讀取和寫入存儲變量,所以在每次迭代時都會發(fā)生昂貴的存儲操作颓芭。這可以通過引入如下的局部變量來節(jié)省GAS來避免顷锰。
好的代碼,例如:
uint sum = 0;
function p3 ( uint x ){
uint temp = 0;
for ( uint i = 0 ; i < x ; i++)
temp += i; }
sum += temp;
3.8.2 其他循環(huán)相關(guān)模式
循環(huán)組合亡问,不好的代碼樣例:
function p5 ( uint x ){
uint m = 0;
uint v = 0;
for ( uint i = 0 ; i < x ; i++) //loop-1
m += i;
for ( uint j = 0 ; j < x ; j++) /loop-2
v -= j; }
loop-1和loop-2可以組合官紫,可以節(jié)省燃氣。
好的代碼樣例:
function p5 ( uint x ){
uint m = 0;
uint v = 0;
for ( uint i = 0 ; i < x ; i++) //loop-1
m += i;
v -= j; }
在這里文章可以找到更多的循環(huán)模式
3.8.3 使用固定大小的字節(jié)數(shù)組
可以使用一個字節(jié)數(shù)組作為byte []州藕,但它在傳入調(diào)用時浪費了大量空間束世,每個元素31個字節(jié)。最好使用bytes床玻。
根據(jù)經(jīng)驗毁涉,對任意長度的原始字節(jié)數(shù)據(jù)使用 bytes標識符,對任意長度的字符串(UTF-8)數(shù)據(jù)使用 string標識符锈死。如果您可以將長度限制為特定的字節(jié)數(shù)贫堰,請始終使用bytes1到bytes32之一,因為它們要便宜得多待牵。
具有固定長度總是節(jié)省GAS其屏。也請參考這個問題描述。
3.8.4 刪除無用的代碼可以在執(zhí)行時節(jié)省GAS
如前面在合同部署中所解釋的那樣刪除無用的代碼即使在執(zhí)行函數(shù)時也會節(jié)省GAS缨该。
3.8.5 在實現(xiàn)功能時不使用庫對于簡單的使用來說更便宜偎行。
調(diào)用庫以獲得簡單的用法可能代價高昂。如果功能在合同中實現(xiàn)簡單且可行贰拿,因為它避免了調(diào)用庫的步驟蛤袒。兩種功能的執(zhí)行成本仍然相同。
4膨更, 參考
(1)區(qū)塊鏈系列十九:Gas優(yōu)化
(2)How to write an optimized (gas-cost) smart contract?
(3)[Solidity] Optimize Smart Contract Gas Usage
(4)What exactly is the Gas Limit and the Gas Price in Ethereum
(5)【易錯概念】以太坊的賬戶汗盘、交易、Gas和Gas Limit的概念