[译]Gas 优化 – 如何优化存储

这篇文章主要介绍了[译]Gas 优化 – 如何优化存储 ,文中通过代码以及文档配合进行讲解,很详细,它对在座的每个人的研究和工作具有很经典的参考价值。 如果需要,让我们与区块链资料网一起学习。

https://www.interchains.cc/17631.html

[译]Gas 优化 – 如何优化存储是很好的区块链资料,他说明了区块链当中的经典原理,可以给我们提供资料,[译]Gas 优化 – 如何优化存储学习起来其实是很简单的,

不多的几个较为抽象的概念也很容易理解,之所以很多人感觉[译]Gas 优化 – 如何优化存储比较复杂,一方面是因为大多数的文档没有做到由浅入深地讲解,概念上没有注意先后顺序,给读者的理解带来困难

[译]Gas 优化 – 如何优化存储

  • 智能合约
  • gas

大多数dApp和游戏都需要将数据存储在区块链blockchain上,因此必须与存储进行交互。 优化智能合约的gas成本是一项重要的工作。

  • 原文链接:https://medium.com/@novablitz/storing-structs-is-costing-you-gas-774da988895e 作者:Nova Blitz
  • 译文出自:登链翻译计划
  • 译者:Tiny熊
  • 本文永久链接:learnblockchain.cn/article…

我们会很快进入一些非常技术性的Solidity概念。

变量合并

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

大多数dApp和游戏都需要将数据存储在区块链blockchain上,因此必须与存储进行交互。 优化智能合约的gas成本是一项重要的工作。

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

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不同于编写现代语言,这是一种不同的”物种" – 以太坊eth区块链blockchain的限制意味着你正在有效地为功能不如1973 Apple 1 。

每一点细微的优化都会帮助你实现更有效的存储方法, 来为你和你的用户节省一些gas。


本翻译由 Cell Network 赞助支持。

  • 发表于 1天前
  • 阅读 ( 41 )
  • 学分 ( 115 )
  • 分类:以太坊eth

部分转自网络,侵权联系删除www.interchains.cchttps://www.interchains.cc/17631.html

区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 !
区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » [译]Gas 优化 – 如何优化存储

提供最优质的资源集合

立即查看 了解详情