Skip to content

Latest commit

 

History

History
173 lines (116 loc) · 5.46 KB

File metadata and controls

173 lines (116 loc) · 5.46 KB

Measurable benefits of EOF

We compared EVM bytecode size, gas usage and execution time of popular mainnet contracts (UniswapV3 and ENS DNSRegistrar). We used Solidity compiler branch which supports most of the EOF features.

TLDR

For Uniswap contracts we achieved benefits in all measured stats.

  • Deployed code and initcode size are 6.5% smaller.
  • Gas usage for deploy and call step is ~14% and ~9% lower.
  • Gas usage for simple swapExact0For1 is also ~5% lower.
  • Execution time on evmone fast EVM implementation also looks better
    • For 100 calls of swapExact0For1 (jump dest analysis enabled for legacy) execution time of EOF bytecode is ~15% shorter than legacy.
    • Benchmarks prepared in a form of state tests (legacy, EOF) (1000 swapExact0For1 calls) also shows measurable benefits from using EOF:
      • EOF: 35ms (~10-15% faster than legacy w/o jump dest analysis)
      • Legacy: 39ms
      • Legacy with jump dest analysis: 68ms

For DNSRegistrar contract test case we also achieved benefits in all metricses.

  • Deploy code and initcode size for the test case are accordingly~6% and ~1.5% smaller.
  • Gas usage for proveAndClaim function call is ~10% lower.
  • Also generated by solc compiler bytecode size of DNSRegistrar contract is ~10% smaller.

Uniswap-v3

Source: https://github.com/ipsilon/solidity/blob/eof-functions-rebased/test/libsolidity/semanticTests/UniswapV3Flattened.sol

Command:

legacy ETH_EVMONE=<PATH_TO_EVMONE_BUILD>/libevmone.dylib ./build/test/./soltest -t "semanticTests/UniswapV3Flattened" -- --optimize --evm-version cancun --show-messages

eof ETH_EVMONE=<PATH_TO_EVMONE_BUILD>/libevmone.dylib ./build/test/./soltest -t "semanticTests/UniswapV3Flattened" -- --optimize --evm-version cancun --eof-version 1 --show-messages

Code size

LEGACY:

init code: 31 202 bytes deployed code: 30 979 bytes

EOF: init code: 29 236 bytes deployed code: 29 048 bytes

Summary:

init code: ~6.5% less deployed code: ~6.5%less

Gas Usage

Gas usage statistics for Uniswap V3 and simple run consists of:

  1. deploy UniswapV3Factory
  2. call runTest
    • deploy ERC20 tokens
    • deploy uniswap pool
    • add liquidity
    • simple swap

LEGACY:

deploy step: 6 832 734 total gas call step: 8 815 561 total

EOF:

deploy step: 5 925 377 total gas call step: 8 094 095 total

Summary:

deploy step: ~14% less call step: ~9% less

Simple Swap swapExact0For1

Simple swap EOF:
gas used:                  77389
gas used (without refund): 80189

Simple swap legacy:
gas used:                  81329
gas used (without refund): 84129

Execution time 100 calls of swapExact0For1

Execution on evmone rev d53b9e21cf39cec7f52253c141ea28572650a949 EOF: 13870 microseconds legacy: 18539 microseconds

Benchmarks for 1000 calls of swapExact0For1 as state tests

Machine:

Apple M3 Pro
36GB RAM

evmone commands: build/bin/./evmone-statetest --trace-summary ./test/evm-benchmarks/benchmarks/main/uniswapv3_many_swaps_legacy.json

build/bin/./evmone-statetest --trace-summary ./test/evm-benchmarks/benchmarks/main/uniswapv3_many_swaps_eof.json

State test jsons: Legacy: https://gist.github.com/rodiazet/28a50d5e67d25e8dd9d87ad5962c9f25 EOF: https://gist.github.com/rodiazet/d9fbdb3f25ca77bb8ede133c1736e318

Summary: Tx Execution EOF: 35ms Legacy: 39ms Legacy with jump dest analysis: 68ms

Additional comment

The code of this contract is bigger than UniswapV3Factory on mainnet because we added for test purpose runTest function which relies on ERC20 contract code and much more. But when running the test with empty runTest function with, EOF the bytecode is smaller by similar % than the one on mainnet.

ENS DNSRegistrar

Custom soltest:

Source: https://github.com/ipsilon/solidity/tree/ens-test

DNSRegistrar semantic test: https://github.com/ipsilon/solidity/blob/ens-test/test/libsolidity/semanticTests/DNSRegistrarFlattened.sol

Code size

LEGACY:

init code: 27 845 bytes deployed code: 3 415 bytes

EOF: init code: 26 171 bytes deployed code: 3 375 bytes

Summary:

init code: ~6% less deployed code: ~1.5%less

Gas Usage

Gas usage statistics for DNSRegistrar::proveAndClaim method call:

LEGACY:

call proveAndClaim: 251 846 total

EOF:

call proveAndClaim: 228 466 total

Summary:

call step: ~10% less gas used for EOF

Deployed code size comparison for clean DNSRegistrar contract

  • legacy optimized: 7 058 bytes
  • eof optimized: 6 388 bytes

Diff ~10%

Compiler flags used:

legacy --via-ir --bin-runtime --optimize eof --via-ir --bin-runtime --experimental-eof-version --optimize