您可能已經遇到過一些 Solidity 技巧來提高您的代碼技能以節省一些 gas,但是,今天我想更多地關注如何理解以太坊虛擬機可以有效地節省您的智能合約的 gas 成本。

因爲我們要深入研究以太坊,所以我將在這裏留下它的黃皮書的片段,其中指定了操作碼的 gas 成本,在文章中我們將參考它們。



提示 1:冷訪問 VS 熱訪問

冷負荷:2100 gas

Gwarmaccess:100 氣體

我們有第一個操作碼,第一個操作碼指定首次訪問變量(或冷訪問)的成本,而第二個操作碼指定第二次及以後訪問變量(熱訪問)的成本。如您所見,價格差異很大,因此瞭解這一點可能會對智能合約交易的成本產生很大影響。讓我們看一個例子。





在 Solidity 中,將數據緩存在函數內可以降低 gas 消耗,即使需要更多行代碼。在這種情況下,通過切換數組的位置,而不是從存儲中使用它,從而每次在循環中都進行冷訪問,它將數組存儲在訪問成本更低的內存中。

提示 #2:零值與非零值以及 gas 退款

Gsset = 20,000 gas

Rsclear = {執行價格折扣}

正如我們在 Gsset 的價格中看到的那樣,在以太坊區塊鏈上將值從 0 更改爲非零是昂貴的,但將值從非零更改爲 0 可以讓你根據操作碼 Rsclear 獲得 gas 值的退款。爲了不利用退款,規定你最多隻能獲得總交易成本 20% 的退款。

你可以在區塊鏈上找到一個非常常見的場景,即更新智能合約中的地址餘額。讓我們看一個例子:





  • 在第一個示例 ZeroToNonZero 合約中,非零到非零(5,000 gas*)+零到非零(20,000 gas)= 25,000 gas

  • 在第二個示例 NonZeroToZero 合約中,非零到零(5,000 gas*)+零到非零(20,000 gas)— 退款(4,800 gas)= 21,200 gas

*2,100 (Gcolssload) + 2,900 (Gsreset) = 5,000 gas

提示#3:狀態變量的順序很重要

存儲就像一個鍵值數據結構,它保存 Solidity 智能合約的狀態變量值。

您可以將存儲視爲一個數組,這將有助於直觀地理解這一點。此存儲“數組”中的每個空間稱爲一個槽,可容納 32 個字節(256 位)的數據,並且智能合約中聲明的每個狀態變量將根據其聲明位置和類型佔用一個槽。

並非所有數據類型都會佔用每個插槽的全部 32 個字節,因爲有些數據類型(bool、uint8、address……)佔用的字節數少於這個數字。

這裏的技巧是,如果兩個/三個或更多變量加起來不超過 32 個字節,solidity 的編譯器將嘗試將它們打包在一個插槽中,但這些變量需要彼此相鄰地定義。





這裏我們使用的數據類型爲 bool (1 字節)、address (20 字節)和 uint256 (32 字節)。因此,瞭解這些變量的大小後,您可以輕鬆理解,在 TwoSlots 合約的第一個示例中,由於 bool 和 address 加在一起(1 + 20 = 21 字節,小於 32 字節),因此它們將佔用一個插槽。在 ThreeSlots 合約中,由於 bool 和 uint256 不能位於同一個插槽中(1 + 32 = 33 字節,大於插槽容量),因此我們總共將使用三個插槽。

那麼,爲什麼這如此重要?

SLOAD 操作碼消耗 2100 gas,用於從存儲槽讀取,因此如果您可以將變量存儲在更少的槽中,最終將節省一些 gas。

提示 #4:uint256 比 uint8 便宜

我們在技巧 3 中瞭解到,uint256(256 位 = 32 字節)本身佔用一個位置,並且我們還了解到 uint8 小於 32 字節。因此,雖然 8 位小於 256 位是顯而易見的,但爲什麼 uint256 更便宜呢?

爲了理解這一點,重要的是要知道,如果一個變量沒有填滿整個槽,並且如果這個槽沒有被任何其他變量填充,那麼 EVM 將用“0”填充剩餘的位,以便能夠對其進行操作。

EVM 執行的這個“0”的加法是會消耗 gas 的,也就是說,爲了節省交易 gas,最好使用 uint256 而不是 uint8。

__________________

希望您在瞭解這些降低智能合約中的 gas 成本的技巧的同時,也能瞭解一些 EVM 的工作原理。

__________________

Twitter @TheBlockChainer 可以找到更多關於智能合約、Web3 安全、Solidity、審計智能合約等的每日更新。

__________________