智能合约调优
用solidity优化器以正确的方式优化你的智能合约的一个简单方法。
翻译:团长(https://twitter.com/quentangle_)
用solidity优化器以正确的方式优化你的智能合约的一个简单方法。
什么是Ethereum中的Gas?
Gas指的是衡量在Ethereum网络上执行特定操作所需的计算量的单位。
由于每笔以太坊交易的执行都需要计算资源,所以每笔交易都需要费用。Gas指的是在以太坊上成功进行交易所需的费用。
gasUsed
为交易分配的最大gas以及最终使用的数量。普通ETH转账涉及21,000个gas单位,而合约涉及的价值更高。
gasPrice
由拍卖型机制决定,矿工寻找交易附带的最高费用,然后从那里按降序处理这些交易。
gas价格随着时间的推移而大幅波动,在活动频繁的时期自然较高,而在网络利用率低的时期则会下降。大多数以太坊钱包提供gas价格的一般参考,并与不同gwei
的处理时间进行比较。
gasLimit
这指的是你愿意花在交易上的最大gas量。虽然你可以调整你的交易将花费多少汽油,但重要的是要小心这样做。这是因为与Ethereum区块链的不同类型的互动将需要不同数量的gas来完成。
blockSize
每个区块的目标大小为1500万gas,但区块的大小会因网络需求而增加或减少,直到区块限制为3000万gas(2倍目标区块大小)。区块中所有交易消耗的gas总量必须小于区块gas上限。这一点很重要,因为它确保了区块不能任意变大。如果区块可以任意变大,那么由于空间和速度的要求,性能较差的全节点将逐渐不再能够跟上网络的发展。
参考:
- https://coinmarketcap.com/alexandria/glossary/gas-price
- https://ethereum.org/en/developers/docs/gas/
- https://ethereum.org/en/developers/docs/blocks/#block-size
什么是优化器Optimizer,很神奇吗?
总的来说,优化器试图简化复杂的表达式,从而减少代码大小和执行成本,也就是说,它可以减少合约部署以及对合约进行外部调用所需的gas。它还会对函数进行特化或内联。特别是函数内联是一个可能导致更大的代码的操作,但它经常被使用,因为它可以有简化的机会。
运行次数不是指优化器的运行频率或优化器的迭代次数,而是指你期望在几年内调用该智能合约中的函数的次数,你运行的时间越长(大量),部署后的成本就越低。你应该知道的权衡是,运行时省钱,但是部署的时候可能更费钱。这取决于你的观点,你是需要对规模进行优化还是对速度进行优化。
Reference:
- https://docs.soliditylang.org/en/v0.8.12/internals/optimizer.html
- https://github.com/ethereum/solidity/issues/2245#issuecomment-302039418
如果代码没有被优化,可能发生的效果
- 交易的成本取决于代币的价格,如果你的智能合约代码没有经过优化,没有效率,它很可能产生比优化后的合约更高的成本。
- 在第一层(L1)上产生瓶颈,超过百分之五十的区块空间会被浪费在非优化的智能合约交易上。
- 交易在交易池中被严重卡住和推迟,一些gas费低的交易会从交易池中被剔除,很可能会失败。
- 此外,如果用户一直受到高gas的挑战,为确保该交易将被选入区块中,会提高gas价格,这种现象会出现在以太坊和许多的拥有高用户流量的区块链网络中。
用简单的数学思想进行粗调
调优前需要知道的因素
- 默认的智能合约代码大小,以Kilobytes(kB)为单位。(缩减大小)
- 在执行你所关心的函数时,默认的最大或平均gas使用量(减少gas使用量)
我们将应用二分搜索Binary search算法来寻找优化器的最小运行值。缩减智能合约的优化实例
S1 = 在优化器关闭的情况下编译的智能合约大小(kB)
n = abs(2²-(S1 * 2))if S₁ = 8.39 kB
round(12.78) = abs(2²-(8.39 * 2))
MyToken20_1
在没有优化器的情况下编译MyToken20_6
在13
次运行中用优化器编译
我们可以从结果中看到MyToken20_6
,与MyToken20_1
相比,智能合约的大小减少了一半。减少交互交易的气体使用的优化实例
S1 = 关闭优化器后编译的关注函数的气体使用量
n = abs(0.02²-(S1 * 0.02))if S1 = 54805 gas used (Max)
round(1096.0996) = abs(0.02²-(54805 * 0.02))if S1 = 39415 gas used (Avg)
round(788.2996) = abs(0.02²-(39415 * 0.02))
MyToken20_1
不适用优化器编译 (最高)MyToken20_5
使用优化器运行 1096
次MyToken20_6
使用优化器运行 788
次 (最低)
根据gas报告表,MyToken20_6
在788
次运行中取得了有意义的结果,使得执行成本与MyToken20_5
的1096
次运行相似,并且在减少部署成本方面取得了更好的结果。为什么 "2 "用于优化缩减大小
"1"是为了减少大小,而不担心执行成本,"2"创造了一个更好的执行成本结果,而代价是增加部署中使用的少量gas。所以你可以先使用"2"。为什么 "0.02"用于优化执行成本?
"0.02"是用来减少gas单位的。另外,你可以稍微改变2或0.02,以找到你喜欢的最近的更好的优化器运行值。
微调需要耐心
看起来,粗调是不够的,有局限性。在微调要更进一步,你需要花多一点耐心在上面。你需要用一个例子来定义你自己的需求,比如说下面的问题
- 智能合约是否被频繁使用 (Yes/No)
- 智能合约是否主要是写入数据 (Yes/No)
- 一个智能合约是否主要被其他智能合约所调用? (Yes/No)
- 智能合约是否主要用来调用其他智能合约? (Yes/No)
- 智能合约是否大于24KB? (Yes/No)
- 智能合约是为短期使用而设计的,不是为长期使用而设计的?(Yes/No)
场景#1: 如果答案1., 2., 3., and, 4是yes
的,你可以推测运行次数应该很高,而且你的智能合约被频繁使用,你需要减少与智能合约互动时的每笔交易成本,但不能太高,如果你的代码大小已经很大了,它会造成可怕的部署成本,大小可以超过Ethereum主网允许你部署的。
https://eips.ethereum.org/EIPS/eip-170
场景#2: 如果问题1.、5.的答案是no
的,而6.的答案是yes
的,你需要使用运行时花费更多的优化器运行。部署成本应尽可能低,以减少智能合约的大小,以节省部署的成本。
提示和技巧场景#1: 略微增加优化器运行的价值场景#2: 略微减少优化器运行的价值
创建项目
1. 前置条件:
你必须先安装必备的软件yarn install
来设置项目。
- nodejs
- npm
- yarngit clone https://github.com/MASDXI/tutorial-smart-contract-optimizer.gityarn install
2. 编译:
An example of an ERC20 token contract
1_MyToken20.sol
compile with disable
the optimizer2_MyToken20.sol
compile with optimizer 200
runs (default
)3_MyToken20.sol
compile with optimizer 50000
runs (satisfied
)4_MyToken20.sol
compile with optimizer 1M
runs (unsatisfied
) 5_MyToken20.sol
compile with optimizer 788
runs (coarse-tune
) 6_MyToken20.sol
compile with optimizer 684
runs (fine-tune
)yarn compile
smart-contract compiled successfully
3. Testing:yarn test
An example of simple testing of the ERC20 smart contract
This article won’t cover all of the test cases, we focused on benchmarking the transfer function and common functions that are frequently used only.
4. Benchmarking:
hardhat.config.js
Objective: Affordable deployment costs and after deployment costs,
We will benchmark all the smart contracts with the same testing.
In this case, We pushed it to 1,000 calls while you can adjust the number of calls by yourself, In case the contract is written with an optimized pattern, It’s can be changed from 10 to 100 calls to save your time and not to turn your machine into a heater.
- 10,000 transactions of transfer function
- 1,000 transactions of approve function
- 1,000 transactions of increasedAllowance function
- 1,000 transactions of decreasedAllowance function
- 1,000 transactions of transferFrom functionyarn benchmark
the result after the benchmark
As you can see from the benchmark result from above the unsatisfied
configuration makes a worse deployment cost than the default
and satisfied
. However, the unsatisfied
slightly decreased cost in execution transactions seems not worth it compared to the deployment cost that increases around ~1.0% from default
, The coarse tune
makes significant results in lowering both costs. While in the fine-tuning method cost of deployment is possible to make a higher or slightly a bit different than the coarse tune because of fine-tuning trying to downsize a little bit more without affecting the execution costs. And lastly sorry if there is any error here.
credit: https://pics.me.me/thumb_when-you-try-to-put-big-turbo-in-your-100k-65698443.png
Conclusion
- 🧙♂️ Keep in mind optimizer isn’t magical, you also need efficiencies, security, and, a clean code mindset to develop a high-quality smart contract.
- ⏱️ Don’t forget to prioritize your time for the performance testing in your development process.
- 🧑💼 Finding sweet spotted is worth it like the best suit is not come from off the rack, If you have time take a Made to Measure (Coarse tune) or Bespoke (Fine tune).
- 💯 Made it close to perfect as possible. It’s (smart contract) can be upgradable but trust me you don’t want to frequently upgrade your smart contract more than twice a week.
- ♻️ Optimizing is not just saving your cost or your user cost it’s saving the network.
Reading Further
原文:https://coinsbench.com/tuning-your-smart-contract-d43c02bdba8