[译]在Solidity中如何优化Gas第一部分:变量
- 译文出自:登链翻译计划
- 译者:DIFENG
- 校对: Tiny熊
本文基于Solidity 0.5.8版本
Gas优化是开发以太坊智能合约所面临的一个独特挑战。要想成功,我们需要学习solidity如何在幕后处理变量和函数。
因此我们将Gas优化分为两部分
在第一部分中,我们通过学习如何权衡变量打包和数据类型。
在第二部分中,我们通过学习可见性、减少执行和减少字节码来优化Gas。
我们所介绍的一些技术将可能违反众所周知的代码模式。在优化之前,我们应该始终考虑可能产生的技术债务和维护成本。
优化变量
变量打包
Solidity合约用连续32字节的插槽来储存。当我们在一个插槽中放置多个变量,它被称为变量打包。
变量打包就像俄罗斯方块游戏。如果我们试图打包的变量超过当前槽的32字节限制,它将被存储在一个新的插槽中。我们必须找出哪些变量最适合放在一起,以最小化浪费的空间。
因为使用每个插槽都需要消耗Gas,变量打包通过减少合约要求插槽数量,帮助我们优化Gas的使用。
我们来看个例子
uint128 a;
uint256 b;
uint128 c;
这些变量无法打包。如果b
和a
打包在一起,那么就会超过32字节的限制,所以会被放在新的一个储存插槽中。c
和b
打包也如此。
uint128 a;
uint128 c;
uint256 b;
这些变量是可以被打包的。因为c
和a
打包之后不会超过32字节,他们可以被存放在一个插槽中。
在选择数据类型时,留心变量打包,如果刚好可以与其他变量打包放入一个储存插槽中,那么使用一个小数据类型是不错的。如果uint128
不能被打包,那么选择uint256
数据位置
变量打包只发生在存储中,内存或者调用数据是不会打包的。打包函数参数或者本地变量对节省空间是没有帮助的。
引用数据类型
结构和数组经常会被放在一个新的储存插槽中。但是他们的内部数据是可以正常打包的。一个uint8
数组会比uint256
数组占用更小的空间。
在初始化结构时,分开赋值比一次性赋值会更有效。分开赋值使得优化器一次性更新所有变量。
初始化结构如下:
Point storage p = Point()
p.x = 0;
p.y = 0;
而非如下:
Point storage p = Point(0, 0);
继承
当你扩展一个合约时,在子合约中的变量可以同父合约中的变量一起打包。
变量的顺序是由C3 linearization决定的。大部分的情况下,你只要知道子合约变量都在父合约变量之后。
数据类型
在选择数据类型以优化Gas时,我们必须权衡利弊。相同的数据类型在不同的情况会也会有便宜或昂贵之分。
内存和存储
在内存中进行运行或者调用数据(同内存中运行一样),都是比存储便宜的。
减少存储操作的一种常见方法是在分配给存储变量之前,对本地内存变量其进行操作。
我们经常看到这样的循环:
uint256 return = 5; // assume 2 decimal places
uint256 totalReturn;
function updateTotalReturn(uint256 timesteps) external {
uint256 r = totalReturn || 1;
for (uint256 i = 0; i < timesteps; i++) {
r = r * return;
}
totalReturn = r;
}
在calculateReturn
函数中,我们使用本地内存变量r
用来存放中间变量,在最后将给过赋值给totalReturn
。
固定和动态
固定大小的数组变量一般比变长数组变量便宜
如果我们知道一个数组有多少元素,我们优先采用固定大小的方式:
uint256[12] monthlyTransfers;
同样的道理也适用于字符型,一个string
或者bytes
变量是变长的。如果一个字符很短,我们可以使用byte32
如果我们必须需要一个动态数组,最好将函数设计成加,而不是减的。消耗固定的Gas,而截断数组消耗Gas线性增长。
映射和数组
大多数的情况下,使用映射会优于数组。
但是,如果是使用较小的数据类型,数组是一个不错的选择。数组元素会像其他存储变量被打包,节省的存储空间可能会弥补更昂贵数组操作。这个方法在处理大型数组时很有用。
其他方式
在处理变量时,还有一些其他技术可以帮助我们优化Gas成本。
初始化
在Solidity中,每个变量的赋值都要消耗Gas。在初始化变量时,我们经常会设置永远不会使用的默认值。
uint256 value;
is cheaper than uint256 value = 0;
.uint256 value;
比uint256 value = 0;
更便宜。
Require字符串
如果你在require中增加语句,你可以通过限制字符串长度为32字节来降低Gas消耗。
不打包变量
以太坊虚拟机一次处理32字节,变量大小小于32字节的会被转化。如果你打包变量没有节省Gas,那么直接使用uint256
会更便宜。
删除
当我们删除变量时,以太坊会给我们退款。它的目的是为了鼓励节约区块链上的空间,我们用它来减少交易的Gas成本。
删除一个变量可以退15,000起,最高可达交易消耗Gas的一半。使用“delete”关键字进行删除相当于为数据类型分配初始值,比如为整数分配“0”。
储存在事件中
那些不需要在链上被访问的数据可以存放在事件中来达到节省Gas的目的。
虽然可以这个操作,但不推荐使用——事件并不是用于数据存储。如果我们需要的数据存储在很久以前发出的事件中,由于需要搜索的块数量太多,获取这个数据可能会非常耗时。
本翻译由 Cell Network 赞助支持。
原文链接:https://medium.com/coinmonks/gas-optimization-in-solidity-part-i-variables-9d5775e43dde
作者:Will Shahda
- Oracle 12.2中的一个参数说明(r12笔记第76天)
- Golang语言社区--【游戏服务器知识】多线程并发
- 用100行Nodejs代码写微博爬虫
- MySQL无法创建表的问题分析(r12笔记第73天)
- Golang语言社区--【H5游戏开发基础知识】JavaScript 用法
- Oracle中的PGA监控报警分析二(r12笔记第87天)
- Oracle 12c PDB的数据备份恢复(r12笔记第84天)
- MySQL和Oracle中唯一性索引的差别(r12笔记第83天)
- 如何用JavaScript进行数组去重
- Oracle 12.1升级到12.2的两种方法(r12笔记第92天)
- Oracle数据库重启后密码失效的问题(r12笔记第91天)
- Oracle和MySQL竟然可以这么写这样的SQL?(r12笔记第99天)
- Golang语言社区--了解C++ 用libcurl库进行http通讯网络编程
- Golang语言-- 小技巧
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 数据库编程 MySQL 常见异常和解决办法
- 持续集成利器,GitHub Actions
- 已拿Offer!百度、字节跳动等后台面经
- Python Networkx基础知识及使用总结
- AkShare-沪深港通持股-个股排行
- Python SQLite常见异常及解决办法
- IPC—进程间通信的基础概念
- Python Django开发 经验技巧总结(一)
- Python SQLite 基本操作和经验技巧(二)
- AkShare-股票数据-主要股东
- 进程间通信的方式(1)
- Python Django开发 经验技巧总结(二)
- 进程间通信的方式(2)
- AkShare-股票数据-机构持股
- View的滑动方式 详细介绍