[译]Gas 优化 - 若何优化存储

币安买币 1 12
变量合并

在Solidity(用于以太坊智能合约的编程语言)中,你拥有“内存(memory)”(想像计算机上的RAM)和“存储(storage)”(想像硬盘驱动器)。 两者均以32字节的块为操做单元(一个字节大约是一个字母)。 在Solidity 中,内存价格廉价(存储或更新值仅需要 3 gas)。 存储很高贵(存储新的值需要20,000 gas,更新值需要 5000 gas)。

大大都dApp和游戏都需要将数据存储在区块链上,因而必需与存储停止交互。 优化智能合约的gas成本是一项重要的工做。

那是一个简单的区块链游戏可能存储的数据:

address owner; uint64 creationTime; uint256 dna; uint16 strength; uint16 race; uint16 class;

若是我们只是间接存储那些值,则可能需要施行以下操做:

mapping(uint256 = address) owners; mapping(uint256 = uint64) creationTime; mapping(uint256 = uint256) dna; mapping(uint256 = uint16) strength; mapping(uint256 = uint16) race; mapping(uint256 = uint16) class;

存储那些数据需要破费120,000 gas(12万),那确实十分高贵! 当我们构造一个构造体并将其存储时,我们会得到更好的成果:

struct GameCharacter { address owner; uint64 creationTime; uint256 dna; uint16 strength; uint16 race; uint16 class; mapping(uint256 = GameCharacter) characters;

如今,当我们存储此构造体时,我们付出的费用更少:75000 gas 。 编译器会将 owner 和creationTime 巧妙地打包到统一插槽中,因而破费25,000,而不是40,000 (译者注,因为第2个写值被当做更新)。 同样效果却更少的破费,不外,我们能够做得更好:

struct GameCharacter { address owner; uint48 creationTime; uint16 strength; uint16 race; uint16 class; uint256 dna; mapping(uint256 = GameCharacter) characters;

新代码的破费:60,000 gas! 我们在此处停止了两项更改:起首,将dna移至末尾。 它是一个uint256,已经是32个字节,因而不克不及包罗任何其他的内容。 然后,我们将 creationTime 的类型从uint64 更改为uint48。 那使前五个字段全数打包成32个字节。 时间戳记没必要超越uint48 类,并且Solidity(与其他大大都语言差别)允许uint为8的肆意倍数,而不单单是8/16/32/64等(若是你迫切地需要空间, 你能够将时间戳利用uint32,在2106年它可能招致问题之前,你可能已经死了 :) 。

我们将费用减半了 -- 很好,对吧? 好吧,不 -- 我们希望所有功用的 gas 消耗都尽可能小,而且仍能够通过将前5个数据字段编码为单个 uint256 来降低成本:

mapping(uint256 = uint256) characters; mapping(uint256 = uint256) dnaRecords; function setCharacter(uint256 _id, address owner, uint256 creationTime, uint256 strength, uint256 race, uint256 class, uint256 dna) external uint256 character = uint256(owner); character |= creationTime 160; character |= strength 208; character |= race 224; character |= class 240; characters[_id] = character; dnaRecords[_id] = dna; function getCharacter(uint256 _id) external view returns(address owner, uint256 creationTime, uint256 strength, uint256 race, uint256 class, uint256 dna) { uint256 character = characters[_id]; dna = dnaRecords[_id]; owner = address(character); creationTime = uint256(uint40(character 160)); strength = uint256(uint16(character 208)); race = uint256(uint16(character 224)); class = uint256(uint16(character 240));

将数据存储为 uint256 只会破费40,000多gas -- 仅停止两次存储操做,再加上一些移位和按位或运算。 考虑到我们最后为120,000gas,那是一个很大的前进! 利用此办法检索数据也要廉价一些。 请留意,那两个函数不会施行任何错误查抄-你需要本身施行此操做,以确保所有输入的最末值都不会超越其更大值(但你必需在所有那些实现中停止不异的查抄)。

编码与解码

上面的代码中可能有一些字符让你觉得目生。 让我们来一探事实:

|=

那是按位或赋值运算符。 用来组合两个二进造值(我们在计算机上,所以一切都是二进造的),办法是“若是此中任一位为1,则成果中的该位为1”。 我们能够在那里利用它,因为我们以uint256(address)开头,那意味着我们晓得在位160之上的所有位均为0。

那是左移位运算符。 它取一个数字的位,然后将它们向左挪动。 因而,creationTime 160 并将creationTime移至成果代码中的插槽160–207中。 将移位和按位或赋值运算相连系,就能够构建编码。

那是右移位运算符。 它的工做体例与 类似,但标的目的相反。 我们能够利用此办法从编码数据转换回来。 但是,我们还需要:

uint256(uint48())

那操纵了solidity编译器的功用。 若是将uint256转换为uint48,则会丢弃所有高于位 48 的位。 那关于我们的目标而言是完美的,因为我们晓得 creationTime 的长度为48位,因而仅提取所需的数据。

利用那些手艺,我们能够显着进步智能合约的性能。

利用数据

如今你已经有了数据存储,你可能需要在函数之间传递数据。 除非你的应用法式像那里描述的那样简单,不然你将碰到16个部分变量的仓库限造。 因而,你需要将数据做为构造体传递到内存中。 此构造体看起来与之前显示的构造略有差别:

struct GameCharacter { address owner; uint256 creationTime; uint256 strength; uint256 race; uint256 class; uint256 dna; function getCharacterStruct(uint256 _id) external view returns(GameCharacter memory _character) { uint256 character = characters[_id]; _character.dna = dnaRecords[_id]; _character.owner = address(character); _character.creationTime = uint256(uint40(character 160)); _character.strength = uint256(uint16(character 208)); _character.race = uint256(uint16(character 224)); _character.class = uint256(uint16(character 240)); }

所有字段均为uint256。 那是 solidity 中最有效的数据类型。 内存中的变量(以至是构造体)底子没有打包,因而在内存中利用uint16不会获得任何益处,并且因为solidity必需施行额外的操做才气将uint16转换为uint256停止计算,所以你也许会丢失标的目的。

我们确其实1980年代早期编写了一个兔子洞编程-对数据停止编码,需要存眷我们能够从代码中抽出的每一个小优化。 编写Solidity差别于编写现代语言,那是一种差别的”物种" - 以太坊区块链的限造意味着你正在有效地为功用不如1973 Apple 1 。

每一点细微的优化城市帮忙你实现更有效的存储办法, 来为你和你的用户节省一些gas。

本翻译由 Cell Network 赞助撑持。

颁发于 2020-09-28 11:08 阅读 ( 1954 ) 学分 ( 115 ) 分类:以太坊

也许您对下面的内容还感兴趣:

留言1

  1. 一条小冬菱
    回复
    人生如梦啊,希望币圈赶紧涨涨涨!

评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。