ethereum / solidity

Solidity, the Smart Contract Programming Language
https://soliditylang.org
GNU General Public License v3.0
23.46k stars 5.8k forks source link

Optimization possibility of memory usage for loops #9046

Open daejunpark opened 4 years ago

daejunpark commented 4 years ago

[This issue has been discussed with @axic, and reported here to be tracked and further discussed.]

Description

In the current code generation (even with the optimization enabled with the flags --optimize --optimize-runs 5000000), there is still room for improvement of the memory usage for loops.

Specifically, if we have sha256(abi.encodePacked(arg1, arg2)) in a loop, every iteration consumes new memory entries to store the arguments arg1 and arg2, even if the same memory entries can be reused over the whole iterations.

For example, for this loop, the first iteration consumes the following entries to store the arguments:

memory[128:159] := 64 // the size of arguments
memory[160:191] := <the-first-argument>
memory[192:223] := <the-second-argument>

and then the second iteration consumes the following:

memory[224:255] := 64 // the size of arguments
memory[256:287] := <the-first-argument>
memory[288:319] := <the-second-argument>

and so on.

In the end, the total 3072 (= 32 iterations * 96 bytes per iteration) bytes of memory are consumed to compute the sha256 function, while the optimized behavior would consume only the 96 bytes for the whole iterations. This ended up wasting ~300 gas unit.

Environment

Steps to Reproduce

Compile https://github.com/axic/eth2-deposit-contract/blob/r1/deposit_contract.sol with the following command line flags:

$ solc deposit_contract.sol --asm --optimize --optimize-runs 5000000

Then see the generated assembly code for the aforementioned loop (i.e., code blocks between tag_123 and tag_124).

(I'll provide a minimal example later.)

chriseth commented 4 years ago

Note that neither the copmpiler nor optimizer does any lifetime tracking of memory objects. abi.encodePacked returns a memory object and thus could be used further down the road, so lifetime tracking is required here if we do not just want to optimize for a special case. There are proposals to do lifetime tracking in the yul optimizer, though.

chriseth commented 4 years ago

Relates to https://github.com/ethereum/solidity/issues/5107

axic commented 3 years ago

Just like we have a codegen shortcut for assigning hashes to constants (#4024) and address.code.length (#10778), we could have a similar shortcut for the keccak256(abi.encode*..) and keccak256(bytes.concat..) (see #10903) cases.

We may want to be a bit reserved though, as this will incentivise this pattern, and I think we should add this shortcut if we believe in the long term we can have this done efficiently even without the shortcut.