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

Contract code lifetime #1964

Closed
chfast opened this issue Apr 12, 2024 · 2 comments
Closed

Contract code lifetime #1964

chfast opened this issue Apr 12, 2024 · 2 comments

Comments

@chfast
Copy link
Member

chfast commented Apr 12, 2024

First of all, an account code is almost exclusively used for execution. It may be used as data source via EXTCODECOPY but this instruction is rarely used in practice (see example stats).

When a deployed code is loaded from mdbx it is kept by reference (e.g. in IntraBlockState::existing_code_). This is because the db maps its storage to memory and accessing such code is valid until next commit (?). This works as a cache layer and saves a copy.

However, there are some problems related to the maintaining the code this way.

The existing_code_ lives only for a single block because it's lifetime is bounded by ExecutionProcessor in Blockchain::execute_block(). Maybe this should be extended until the next commit?

The evmone cannot use the code in this form. It needs additional preprocessing of the code: padding (for more efficient interpreter loop) and jumpdest analysis.

Silkworm has a LRU cache of size 5000 of such preprocessed code in EVM::analysis_cache used in EVM::execute_with_baseline_interpreter.

Potential improvement: drop LRU cache

After preprocessing the original code is useless. We can do the preprocessing when loading from db and keep the preprocessed form in the existing_code_. This removes the need for LRU cache. However, to my knowledge there is no way to inform mdbx that the value is not needed any more and can be unloaded from memory.

Potential improvement: preprocess code on deployment

More advanced approach is to preprocess the code when it is being deployed (contract creation) and store the preprocessed version in mdbx. Here we can some sub-options:

  1. Just pad the code with 33 null bytes. evmone would need to allocate another buffer for jumpdest analysis.
  2. Combine padded code and jumpdest analysis into a single buffer and store it in db as the preprocessed code. This increases the code size by ~13%.
  3. Modify the option 2 by using more compressed form of jumpdest analysis taken from the Verkle Tree proposal. Here the size overhead is ~3% but this is untested and the performance of executing jump instructions is unknown.
@chfast
Copy link
Member Author

chfast commented Jun 12, 2024

I implemented this PoC where code is analyzed when getting from "db" and kept in this "executable" form in IntraBlockState. Now checking if this is correct by Mainnet sync... #2097

With the original LRU cache of code analysis the analyze() is close to 0% in profile. This modification bumps it to ~1%.

This also doesn't solve the inefficient DB/mdbx interface where to pad the code I need a full copy of it. Having the full copy if the code the mdbx paged original code is wasted.

@chfast
Copy link
Member Author

chfast commented Sep 30, 2024

The conclusion from the Mainnet execution sync benchmark and manual profiling is the Code Analysis cache is effective. The cache can be also used for code, see #2382.

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

No branches or pull requests

1 participant