在 Solidity 中,动态值数组是否比引用数组效率更高吗?
Photo by Nick Kwan[4] on Unsplash[5]
在 Datona 实验室的 Solidity 智能数据访问合约(S-DAC)模板的开发和测试过程中,我们经常需要处理一些像用户ID这样数据小但未知长度的数据。理想情况下,这些数据存储在一个小数值的动态值数组中。
在这篇文章的例子中,我们研究了在 Solidity 中使用动态值数组是否比引用数组或类似解决方案在处理这些小数值时更高效。
当我们有一个由已知的小数值的小数组(长度小)组成的数据时,我们可以在 Solidity 中使用一个数值数组(Value Arrays),在这篇文章[6]中,我们提供并测量了 Solidity 数值数组。得出的结论是,在多数情况下使用数值数组都可以减少存储空间和gas消耗。
得出这个结论是因为Solidity在以太坊虚拟机(EVM)上运行时有 非常大的256位(32字节)机器字长[7]。基于这个特点,再加上处理引用数组时的高gas消耗,让我们考虑使用数值数组。
既然我们可以为固定值数组操作提供自己的库,同样是否也适用于动态值数组呢?
让我们比较一下动态值数组与固定长度值数组以及 Solidity 自己的固定长度数组和动态数组。
我们也将比较两个结构体,一个结构体包含一个数组长度和一个固定数组,另一个结构体包含一个数值数组。
在 Solidity 中,只有 storage 类型有动态数组。memory 类型的数组必须有固定长度,并且不允许使用push()
来附加元素。
我们以 Solidity 库形式为动态值数组提供代码,我们能提供push()
(和pop()
)同时用于 storage 和 memory 数组。
动态值数组需要记录并操作数组的当前长度。在下面的代码中,我们将数组长度在存储在256位(32字节)机器码值的最高位。
下面是一些与 Solidity 可用类型匹配的动态值数组:
Dynamic Value Arrays(动态值数组)
Type Type Name Description
uint128[](1 "") uint128d1 one 128bit element value
uint64[](3 "") uint64d3 three 64bit element values
uint32[](7 "") uint32d7 seven 32bit element values
uint16[](15 "") uint16d15 fifteen 16bit element values
uint8[](31 "") uint8d31 thirty-one 8bit element values
上述我提出的类型名称,它们在会本文中使用,但你可能会有一个更好的命名方式。
下面我们将详细地研究uint8d31
。
很明显,有更多可能的数值数组。假设我们保留最高位的256位来存最大的动态数组长度,X(位数的值)乘以Y(元素个数)必须小于或者等于256减去容纳数组长度的位数(L):
More Dynamic Value Arrays
Type Type Name Len Description
uintX[](Y "") uintXdY L X * Y <= 256 - L
uint255[](1 "") uint255d1 1 one 248bit element value
uint126[](2 "") uint126a2 2 two 124bit element values
uint84[](3 "") uint84d3 2 three 82bit element values
uint63[](4 "") uint63d4 3 four 62bit element values
uint50[](5 "") uint50d5 3 five 51bit element values
uint42[](6 "") uint42d6 3 six 42bit element values
uint36[](7 "") uint36d7 3 seven 36bit element values
uint31[](8 "") uint31d8 4 eight 31bit element values
uint28[](9 "") uint28d9 4 nine 28bit element values
uint25[](10 "") uint25d10 4 ten 25bit element values
uint22[](11 "") uint22d11 4 eleven 22bit element values
uint21[](12 "") uint21d12 4 twelve 21bit element values
uint19[](13 "") uint19d13 4 thirteen 19bit element values
uint18[](14 "") uint18d14 4 fourteen 18bit element values
uint16[](15 "") uint16d15 4 as above
uint15[](16 "") uint15d16 5 sixteen 15bit element values
uint14[](17 "") uint14d17 5 seventeen 14bit element values
uint13[](19 "") uint13d19 5 nineteen 13bit element values
uint12[](20 "") uint12d20 5 twenty 12bit element values
uint11[](22 "") uint11d22 5 twenty-two 11bit element values
uint10[](25 "") uint10d25 5 twenty-five 10bit element values
uint9[](27 "") uint9d27 5 twenty-seven 9bit element values
uint8[](31 "") uint8d31 5 as above
uint7[](35 "") uint7d35 6 thirty-five 7bit element values
uint6[](41 "") uint6d41 6 forty-one 6bit element values
uint5[](50 "") uint5d50 6 fifty 5bit element values
uint4[](62 "") uint4d62 6 sixty-two 4bit element values
uint3[](83 "") uint3d83 7 eighty-three 3bit element values
uint2[](124 "") uint2d124 7 one-hundred & twenty-four 2bit EVs
uint1[](248 "") uint1d248 8 two-hundred & forty-eight 1bit EVs
不同的项目需要特定的数组类型,并且同一个项目可能需要多种数组类型。例如,uint8d31
用于用户ID,uint5d50
用于用户角色。
注意uint1d248
数值数组。它让我们可以有效地将多达248个1位的元素(代表布尔值)编码到1个 EVM 字中。而Solidity相同作用的 bool[248] ,在内存中消耗多 248 倍的空间,在存储(storage)中则多8倍。