plsql存储过程修改后怎么保存_Solidity变量存储位置与gas成本详解
如果你要優化Solidity合約的gas成本,變量的數據存儲位置是第一個 要考慮的因素。在這個教程中,我們將深入學習Solidity中的數據存儲機制, 包含以太坊虛擬機EVM的介紹、Solidity的三種數據存儲位置的 區別以及不同情況下跨區域數據賦值的gas成本分析與利用等內容。
用自己熟悉的語言學習 以太坊DApp開發 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、以太坊虛擬機
在開始探討Solidity的數據存儲之前,我想先介紹下以太坊虛擬機 的一些相關內容,以便更容易理解后續的部分。
EVM的內部結構大致如下圖所示:
當我們安裝以太坊客戶端時,它其中就包含了EVM這個專門用于運行 智能合約的輕量級操作系統。EVM的架構基于棧機器模型,這意味著 其指令集是基于棧而非寄存器來運作的。EVM操作碼清單在黃皮書中 有描述,具體可查閱以太坊虛擬機操作碼和指令參考手冊。
在EVM中指令的執行流程如下:當一個交易觸發智能合約代碼的執行時, 就會實例化一個EVM,EVM的ROM載入了要調用的合約代碼。程序計數器 被清零,存儲從合約賬號對應的部分載入,內存清零,設置區塊和環境 變量,然后代碼開始執行。
2、Solidity變量的數據存儲位置
現在讓我們回到memory關鍵字。從0.5.0版本開始,所有的復雜類型必須 顯式指定其存儲的數據位置,有三種可選的數據位置:memory、storage 和calldata。
注意:唯一可以省略數據位置聲明的是狀態變量,因為狀態變量始終保存 在賬號的存儲中。
storage/存儲
- 存儲中的數據是永久存在的。存儲是一個key/value庫
- 存儲中的數據寫入區塊鏈,因此會修改狀態,這也是存儲使用成本高的原因。
- 占用一個256位的槽需要消耗20000 gas
- 修改一個已經使用的存儲槽的值,需要消耗5000 gas
- 當清零一個存儲槽時,會返還一定數量的gas
- 存儲按256位的槽位分配,即使沒有完全使用一個槽位,也需要支付其開銷
memory/內存
- 內存是一個字節數組,槽大小為256位(32字節)
- 數據僅在函數執行期間存在,執行完畢后就被銷毀
- 讀或寫一個內存槽都會消耗3gas
- 為了避免礦工的工作量過大,22個操作之后的單操作成本會上漲
calldata/調用數據
- 調用數據是不可修改、非持久化的區域,用來保存函數參數,其行為類似于內存
- 外部函數的參數必須使用calldata,但是也可用于其他變量
- 調用數據避免了數據拷貝,并確保數據不被修改
- 函數也可以返回使用calldata聲明的數組和結果,但是不可能分配這些類型
3、Solidity數據位置與賦值成本的研究
如果你不期望合約代碼出現不可預計的行為,重要的一點是理解數據位置的 賦值是如何運作的。
下面列出了不同位置的變量間賦值的一些規則:
- 在存儲和內存(或調用數據)間的賦值將創建一個新的獨立拷貝
- 內存之間的賦值僅創建引用,這意味著對一個內存變量的修改會 同時反應在其他引用相同數據的內存變量上
- 從存儲到局部存儲變量的賦值,實際上只會給一個引用
- 所有其他賦值通常導致產生新的數據拷貝。例如賦值給狀態變量 或位于存儲的結構類型的局部變量成員時,即使局部變量只是一個 引用,也會產生新的數據拷貝
下面讓我們用remix debugger 深入研究一下:
// SPDX-License-Identifier: GPL-3.0pragma solidity ^0.7.0;contract DataLocationTest { uint[] stateVar = [1,4,5]; function foo() public{ // case 1 : from storage to memory uint[] memory y = stateVar; // copy the content of stateVar to y // case 2 : from memory to storage y[0] = 12; y[1] = 20; y[2] = 24; stateVar = y; // copy the content of y to stateVar // case 3 : from storage to storage uint[] storage z = stateVar; // z is a pointer to stateVar z[0] = 38; z[1] = 89; z[2] = 72; } }用上面的代碼創建一個新文件,然后部署合約?,F在試著調用函數, 你將會在控制臺看到交易的詳細信息以及旁邊的debug按鈕。點擊這個 按鈕:
這時應當可以看到調試器區域大致如下:
點擊上圖中紅色標識的箭頭,單步執行代碼。
你應當注意到的第一件事,是存儲載入了stateVar的內容,這正如我們 之前在EVM部分提到的,當然,這里沒有局部變量。
當你繼續單步執行時,你應當會看到變量y出現在局部變量區域(Solidity Locals)。 繼續單步執行,你還會看到需要執行很多字節碼來創建必要的內存空間、 從存儲中載入所有數據并將其拷貝到內存。這意味著需要支付更多的gas, 因此從存儲區域到內存區域的賦值非常昂貴。
現在讓我們研究下第二種情況:從內存區域賦值給存儲區域。 例如當你修改完內存變量后,可能需要將修改存回存儲區域。 這時也會消耗許多gas。如果我們計算debugger中單步執行前后的 剩余gas差,可以看到消耗了17083 gas。該操作用了4個SSTORE 指令:第一個用于保存數組大小,消耗800gas,其他三個用于 更新數組的值,每個消耗5000gas。
接下來讓我們看看第三種情況:從存儲區域賦值給存儲區域。 這一次會創建一個新的局部變量來保存stateVar的值。如果我們 查看代碼的執行過程,就會注意到Solidity做的就是將第一個存儲 槽位的地址推入棧,該槽位保存有數組長度。根據文檔說明,對 動態數組而言,槽的位置包含了數組的長度。
如果我們比較不同情況下將數據拷貝進內存的成本,那么 根據上述情況(更新并拷貝回存儲:21629 gas,創建引用并 直接更新狀態:5085gas),非常清楚的是第二種方案的成本 要低得多。
但是如果我們要直接更新狀態變量,例如:
stateVar[0] = 12;這也是可行的,不過如果你要處理映射和嵌套的數據類型,使用 存儲指針會讓代碼可讀性更強。
原文鏈接:http://blog.hubwiz.com/2020/11/11/solidity-data-storage/
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的plsql存储过程修改后怎么保存_Solidity变量存储位置与gas成本详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机运算速度的具体单位是什么
- 下一篇: jvm内存参数配置_“步步精心”-常用J