Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimizer seems to produce larger bytecode when run longer #2245

Closed
tcoulter opened this issue May 5, 2017 · 8 comments
Closed

Optimizer seems to produce larger bytecode when run longer #2245

tcoulter opened this issue May 5, 2017 · 8 comments

Comments

@tcoulter
Copy link

tcoulter commented May 5, 2017

I found a curious result when upgrading Truffle to Solidity 0.4.11 (via solc-js). It seems that the more the optimizer is run, the longer my bytecode gets. This suggests to me that the optimizer is adding instructions instead of removing them.

The contract in question is this one. This is Truffle's assertion library for its Solidity tests, which is so large that it could possibly overflow the block gas limit. This contract relies on the optimizer to ensure the that it can be deployed free and clear.

When upgrading from Solidity 0.4.8 to 0.4.11, I used the default/example configuration from here to specify my optimizer settings. That configuration, for me, looks like this:

  {
    "language": "Solidity",
    "sources": {
      "Assert.sol": {
        "content": "..."
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 500
      },
      "outputSelection": {
        "*": {
          "*": [
            "abi",
            "ast",
            "evm.bytecode.object",
            "evm.bytecode.sourceMap",
            "evm.deployedBytecode.object",
            "evm.deployedBytecode.sourceMap"
          ]
        },
      }
    }
  }

With this configuration, exactly as it stands, this contract will overflow the block gas limit of 4712388 when deployed. The character length of the resulting bytecode is 35242 characters long. When I increase the number of runs the optimizer makes, curiously, the longer the code becomes. Inversely, if I run the optimizer as little as possible without turning it off, the shorter the resulting bytecode. Here's a list of findings:

Optimizer enabled Optimizer runs Code length (characters) Contract deployable
Yes 0 33900 Yes
Yes 1 to 13 33900 Yes
Yes 14 33908 Yes
Yes 100 34506 Yes
Yes 250 34906 Yes
Yes 400 35242 No
Yes 500 35242 No
Yes 1000 35946 No
Yes 10000 40646 No
No N/A 44380 No

It would appear that the optimizer is optimizing for space/code length when turned on. However, if you run it (i.e., runs > 0), the more instructions or data it appears to add to the resulting bytecode. My only guess is that this is meant to optimize for transaction costs for certain transactions by replacing costly instructions with cheaper, less costly versions (though using more of them), but I haven't looked deeper.

One interesting result that I included in the table: runs 0 through 13 all produce the same code length, but different bytecode. I'm unsure if this is significant, but runs = 14 is where I saw the first change in code length.

For Truffle users, I plan to default to runs = 0 as smaller code (i.e., ability to deploy) is more valuable than slightly cheaper transaction costs. I'll likely let users configure this option in the future.

@chriseth
Copy link
Contributor

The documentation around the runs option for the optimizer could be improved, sorry about that.
Running solc --help displays:

 --optimize-runs n (=200)
                       Estimated number of contract runs for optimizer tuning.

The runs option is not about how often the optimizer is run (it is always run until there is nothing left to improve). Instead, the optimizer has to operate under a trade-off situation: You can optimizer the code for size (i.e. deployment costs) or for runtime costs ("speed"). This trade-off is different in situations where you deploy the code only once, run it and destroy the contract again as opposed to a contract that is deployed and then use multiple thousand times. Because of that, the optimizer allows for a linear factor to balance between these two criteria that roughly represents how often each opcode will be run after the contract has been deployed. Since the costs for deployment matter less and less the more often you run invoke the contract, it will get longer with increasing arguments to the runs option.

@chriseth
Copy link
Contributor

Acceptance criterion: Improve the documentation about the optimizer settings, especially the "runs" parameter.

@gitcoinbot
Copy link

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


This issue now has a funding of 100.0 DAI (100.0 USD @ $1.0/DAI) attached to it.

@gitcoinbot
Copy link

gitcoinbot commented May 2, 2018

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work has been started.

  1. @JonnyBurger

has committed to working on this project to be completed 5 months, 4 weeks from now.

@gitcoinbot
Copy link

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work for 100.0 DAI (100.0 USD @ $1.0/DAI) has been submitted by:
1.

  1. @JonnyBurger

@gdipri01 please take a look at the submitted work:

@chriseth
Copy link
Contributor

chriseth commented May 2, 2018

Fixed in #4048

@chriseth chriseth closed this as completed May 2, 2018
@gitcoinbot
Copy link

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


The funding of 100.0 DAI (100.0 USD @ $1.0/DAI) attached to this issue has been approved & issued to @JonnyBurger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants
@axic @tcoulter @chriseth @gitcoinbot and others