Packed storage and the new optimizer

chrisethchriseth Member Posts: 170 ✭✭✭
By the recent changes in gas prices, the cost for storage slots has increased dramatically relative to the cost of most other operations. Because of that, Solidity now uses storage more sparingly: Instead of putting each item in its own slot, it tries to combine multiple smaller items in a single slot. In the following code, the struct only occupies a single slot, while it would have used four slots in older versions of Solidity.
contract C { struct Data { bool active; bytes3 identifier; uint32 value1; uint32 value2; } Data a; function read() returns (bool active, bytes3 id, uint32 val) { active =; id = a.identifier; val = a.value1 ** 2 + a.value2; } }
This of course makes it harder to access such data. The code that is generated for "id = a.identifier" has to read the storage slot, set all bytes to zero that belong to other variables in the same slot and shift the value by the correct amount of bits to align it correctly again. Storing data is even more complicated: For a statement like "a.value1 = 8;", you have to first read the storage slot, then replace the bytes belonging to "value1" by 8 (but leave all other bytes untouched) and then store the new value again.

In the code example, the compiler will generate a new SLOAD instruction together with shifting logic for each access to the members of a, because code generation is always "local". Since this is totally unnecessary, it made sense to add some features to the optimizer. This new version of the optimizer which is currently on its way to develop analyses the generated assembly code, stores everything in some kind of "expression tree" and then re-creates the assembly from this tree while it tries not to "recomputes" values that have been available earlier. The following assembly is generated by the optimizer for the body of read(). As you can see, there is only a single SLOAD instruction. Note that since the optimizer operates at the assembly level, it is also automatically used by the c++ compilers for sepent and lll. Of course, this is only one example for where the optimizer is useful. In much the same way, it will compute the power only once if you do things like "a = b**20 * b**(19+1);".
JUMPDEST function read() returns (bool active, bytes3 id, uint32 val) {... PUSH 1 PUSH 0 bool active SLOAD SWAP1 DUP2 DIV PUSH FF AND SWAP2 PUSH 100 DUP3 DIV a.identifier PUSH 10000000000000000000000000000000000000000000000000000000000 a.identifier MUL a.identifier SWAP2 PUSH 10000000000000000 DUP2 DIV a.value2 PUSH FFFFFFFF a.value2 SWAP1 DUP2 AND a.value2 PUSH 2 2 PUSH 100000000 SWAP1 SWAP4 DIV a.value1 SWAP2 SWAP1 SWAP2 AND a.value1 SWAP2 SWAP1 SWAP2 EXP a.value1 ** 2 ADD a.value1 ** 2 + a.value2 SWAP1 JUMP [out] function read() returns (bool active, bytes3 id, uint32 val) {...


Sign In or Register to comment.