01
数组绑定检查
我们已经看到固定长度数组与结构和状态变量具有相同的存储布局,但生成的汇编代码是不同的。 原因是Solidity生成数组访问的边界检查。
让我们再次编译数组合约,这次关闭优化:
$ solc --bin --asm c-static-array.sol
下面对组件进行了评论,在每条指令后输出相应状态:
tag_2:
0xc0fefe
[0xc0fefe]
0x5
[0x50xc0fefe]
dup1
/* array bound checking code */
// 5
0x6
[0x60x50xc0fefe]
dup2
[0x50x60x50xc0fefe]
lt
[0x10x50xc0fefe]
// bound_check_ok = 1 (TRUE)
// if(bound_check_ok) { goto tag5 } else { invalid }
tag_5
[tag_50x10x50xc0fefe]
jumpi
// Test condition is true. Will goto tag_5.
// And `jumpi` consumes two items from stack.
[0x50xc0fefe]
invalid
// Array access is valid. Do it.
// stack: [0x5 0xc0fefe]
tag_5:
sstore
[]
storage: {0x5=>0xc0fefe }
我们现在看到绑定检查代码, 我们已经看到编译器能够优化其中的一些东西,但并不是很完美。
在本文的后面,我们将看到数组绑定检查如何干扰编译器优化,使得固定长度数组的效率远低于存储变量或结构。
02
数组压缩
存储是昂贵的, 一个关键的优化是将尽可能多的数据打包到一个32字节的插槽中。
考虑一个包含四个存储变量的合约,每个变量64位,总共加起来为256位(32字节):
pragma solidity ^0.4.11;
contract C {
uint64a;
uint64b;
uint64c;
uint64d;
function C() {
a =0xaaaa;
b =0xbbbb;
c =0xcccc;
d =0xdddd;
}
}
我们希望(希望)编译器使用一个sstore将它们放在同一个存储槽中。
编译:
$ solc --bin --asm --optimize c-many-variables--packing.sol
组合:
tag_2:
/* "c-many-variables--packing.sol":121:122 a */
0x0
/* "c-many-variables--packing.sol":121:131 a = 0xaaaa */
dup1
sload
/* "c-many-variables--packing.sol":125:131 0xaaaa */
0xaaaa
not(0xffffffffffffffff)
/* "c-many-variables--packing.sol":121:131 a = 0xaaaa */
swap1
swap2
and
or
not(sub(exp(0x2,0x80), exp(0x2,0x40)))
/* "c-many-variables--packing.sol":139:149 b = 0xbbbb */
and
0xbbbb0000000000000000
or
not(sub(exp(0x2,0xc0), exp(0x2,0x80)))
/* "c-many-variables--packing.sol":157:167 c = 0xcccc */
and
0xcccc00000000000000000000000000000000
or
sub(exp(0x2,0xc0),0x1)
/* "c-many-variables--packing.sol":175:185 d = 0xdddd */
and
0xdddd000000000000000000000000000000000000000000000000
or
swap1
sstore
需要注意的关键是目前只有一个sstore。证实我们优化成功!
领取专属 10元无门槛券
私享最新 技术干货