0%

StudyRecord-Hitchhikers_Guide_to_the_EVM

Pre:

Alex Roan: Hitchhiker’s Guide to the EVM 学习记录

20220707095117

有很多主题想讨论,但有一个最重要的概念:

Gas Golfing

20220707095719

gas golfing is the process of optimizing the existing functionality of your contract to to do the same things to be able to perform the same functions but just cheaper and more efficiently

高尔夫最终目的就是想打进球洞,过程中需要不断优化去减少杆数
gas优化也是一样,一个函数同样的功能怎么才能更便宜这里特指,优化对storage的使用

What is Storage in Solidity?

定义:

storage is data that is persistent between transactions it’s stored in a contract for extended periods of time and it can be accessed and changed in future transactions

例子:

20220707100134

图中有个storage 变量,在其他编程语言中可能被称为instance variablesclass variables

but in solidity there is a catch so it is one of the most gas-intensive things that a contract can play around with accessing it changing it is disproportionately expensive in comparison with what we associate with normal disk storage

不同之处在于,在别的编程语言中,持久化存储变量可能只是占用硬盘空间,但在solidity中持久化存储变量,在读写这类变量的数据都跟钱有很大关系。

Why is Storage Expensive?

中心化世界:

20220707100745

在中心化的世界中,持久化存储的特点:

  • 单个硬盘

  • 总成本低

  • 易扩容

去中心化世界:

20220707101232

换到去中心化的世界中,存储不再是存在一个服务器的一个硬盘里,而是存在整个网络里的每一块硬盘上。
Evm里有两个很贵的op code

  • SSTORE: store this piece of data in this storage slot

  • SLOAD: read this data from this specific slot any time that we set a storage variable in a function somewhere

省gas的五个方法:

20220707101850

非必要的话,不存Storage:

20220707102201

20220707102425

一般情况下,合约都不会再次使用这部分数据(转账记录),所以用event代替Storage

20220707102454

对于gas的影响:

20220707102925

尽量用constant或immutable:

20220707104558

有不变量就尽量用constantimmutable用关键字constantimmutable会影响opcode slot

  • constant:constant value must be hard coded into the declaration rather than set in the constructor

  • immutable:set it in the constructor but as soon as that constructor is finished that value that we set for link can never change again只能仅仅一次在构造函数中设置值

区别在于初始化赋值的位置不一样。

gas影响:

20220707105356

用了不变量关键词后,读取一次数据对gas的影响。如果经常会读取到该变量,那么累计下来会节省很多gas

自己测试:

部署:

1
2
3
4
5
6
7
8
9
10
// constant1.sol
pragma solidity ^ 0.8.11;

contract A {
address public LINK;

constructor(address tokenAddr) {
LINK = tokenAddr;
}
}

耗费gas:133322

1
2
3
4
5
6
7
8
// constant2.sol
pragma solidity ^ 0.8.11;

contract A {
address public constant LINK = 0x302c98e6d6A65Bf15255b81972f9EaA1F45438C8;

constructor() {}
}

耗费gas:104187

1
2
3
4
5
6
7
8
9
10
// constant3.sol
pragma solidity ^ 0.8.11;

contract A {
address public immutable LINK;

constructor(address tokenAddr) {
LINK = tokenAddr;
}
}

耗费gas:110824

读取:

  • constant1.sol:耗费gas 23553

  • constant2.sol:耗费gas 21420

明确表示Storage Variable:

20220710093400

20220710093440

后面写函数的时候,提醒自己在操作的是Storage的变量,会比较耗费gas

不要频繁读写:

20220710093801

状态变量在循环、多条件判断的时候,注意观察接触touch状态变量的次数,读写超过两次(Slod+SStore)

做法:先把状态变量加载到内存里的临时变量,再去频繁读写

20220710094037

提及了EIP-2929里的Cold and Warm的概念:读过的slot后,状态:cold->warm,下次读写会更便宜。
EIP2929, EIP2930 簡介

在实作层,EVM会维系一个本笔交易读取过所有交易的Set。每次有尚未读取过的slot时,就会先收取一笔CLOD_SLOAD_COST (2100),然后把这个slot加入这个set中,下次读写就会比较便宜。

对于已经读取过的Slot,再次写入的OpcodeSSTORE之gas cost为会降低为
5000 — COLD_SLOAD_COST (2100) = 2900

简单的说,单纯只操作一次SSTORE的总gas 会维持一样在5000 。但如果这个slot是之前有读过的,则写入的gas cost就会降低。近一步来说,一个x += 100,其实会变得更便宜:

Pre-EIP-2929: 800 SLOAD + 5000 SSTORE = 5800
Post-EIP-2929: 2100 SLOAD + 2900 warm SSTORE = 5000

gas影响;

20220710095126

  • warm slot还是不如在内存里读取便宜

  • 可能每次仅省了一丢丢gas,成千上万的tx就会显现出gas优化的好处

Pack Your Struct 打包结构体:

20220710095620

在EVM中,32是一个重要的数字,要记住!

20220710095827

20220710100155

评估一下第二个变量,如果存的内容较小,就可以定义长度小一点的。然后evm就会把两个变量压缩在slot1里面。

20220710102044

squeezes 挤挤总会有的

20220710103109
20220710141301

对变量适当的排序、选择适当的size,可以减少slot的使用

不知道有没类似的工具可以提示优化的

Refs: