第三十課 以太坊智能合約solidity如何節(jié)省GAS費缠劝?

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ù)字段:

input_remix

這兒是 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的概念

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末询一,一起剝皮案震驚了整個濱河市隐孽,隨后出現(xiàn)的幾起案子癌椿,更是在濱河造成了極大的恐慌,老刑警劉巖菱阵,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踢俄,死亡現(xiàn)場離奇詭異,居然都是意外死亡晴及,警方通過查閱死者的電腦和手機都办,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虑稼,“玉大人琳钉,你說我怎么就攤上這事≈刖耄” “怎么了歌懒?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溯壶。 經(jīng)常有香客問我及皂,道長,這世上最難降的妖魔是什么且改? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任验烧,我火速辦了婚禮,結(jié)果婚禮上又跛,老公的妹妹穿的比我還像新娘碍拆。我一直安慰自己,他們只是感情好慨蓝,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布倔监。 她就那樣靜靜地躺著,像睡著了一般菌仁。 火紅的嫁衣襯著肌膚如雪浩习。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天济丘,我揣著相機與錄音谱秽,去河邊找鬼。 笑死摹迷,一個胖子當著我的面吹牛疟赊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播峡碉,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼近哟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鲫寄?” 一聲冷哼從身側(cè)響起吉执,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疯淫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后戳玫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熙掺,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年咕宿,在試婚紗的時候發(fā)現(xiàn)自己被綠了币绩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡府阀,死狀恐怖缆镣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情试浙,我是刑警寧澤董瞻,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站川队,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏睬澡。R本人自食惡果不足惜固额,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煞聪。 院中可真熱鬧斗躏,春花似錦、人聲如沸昔脯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽云稚。三九已至隧饼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間静陈,已是汗流浹背燕雁。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲸拥,地道東北人拐格。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像刑赶,于是被迫代替她去往敵國和親捏浊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 原文:Smart contracts 正如我們在[intro]中看到的那樣撞叨,以太坊中有兩種不同類型的帳戶:外部擁有...
    Jisen閱讀 4,941評論 1 7
  • 原文:Introduction 控制和責任 像以太坊這樣的開放式區(qū)塊鏈是安全的金踪,因為它們是去中心化的浊洞。這意味著以太...
    Jisen閱讀 6,252評論 0 7
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭热康,有人歡樂有人憂愁沛申,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,536評論 28 53
  • 信任包括信任自己和信任他人 很多時候姐军,很多事情铁材,失敗、遺憾奕锌、錯過著觉,源于不自信,不信任他人 覺得自己做不成惊暴,別人做不...
    吳氵晃閱讀 6,190評論 4 8
  • 怎么對待生活辽话,它也會怎么對你 人都是哭著來到這個美麗的人間肄鸽。每個人從來到塵寰到升入天堂,整個生命的歷程都是一本書油啤,...
    靜靜在等你閱讀 4,976評論 1 6