06.20.2024|DaniPopesGeorgios Konstantopoulos
We recently published Reth’s high performance roadmap, calling for broader usage of “gas per second” as a performance metric in EVM blockchains, and detailing our plan to scale blockchains to 1 gigagas per second and beyond, a 1000x improvement from the status quo of Ethereum.
Today, we’re excited to open source revmc
, a compiler for lowering EVM Bytecode into native code, demonstrating anywhere from 1.85x to 19x improvements in various realistic EVM benchmarks. We also integrated revmc
in Reth and successfully synced the chain. Next up, we’re going to integrate revmc
in OP Reth for L2 usage, where its improvements will shine in computationally heavy workloads.
Revmc is extensively tested, and will be further polished for production usage. The code is open source under the Apache/MIT license at github.com/paradigmxyz/revmc.
The development of revmc is motivated by the desire to enhance the performance of the EVM, due to the inherent limitations of bytecode interpretation. Traditional EVM execution involves sequentially processing instructions through an interpreter, introducing significant overhead and latency because the instructions do not execute as native assembly code. By compiling EVM bytecode into optimized native machine code, the compiler enables direct execution on hardware, drastically reducing the overhead associated with virtual machine layers.
Furthermore, compiling bytecode ahead of time (AOT) rather than just-in-time (JIT) during execution mitigates security risks associated with JIT compilation, such as vulnerabilities to malicious code designed to exploit the JIT process. The AOT approach allows for the highest demand contracts to be pre-compiled and stored securely, ensuring that the blockchain operates efficiently without compromising on security.
These ideas have existed for a long time outside of crypto, e.g. in the case of Java or WASM’s JIT compiler, and inside of crypto for the EVM but also other runtimes as well. We expect every blockchain’s runtime will have a compiled native assembly version of its runtime to provide higher performance.
Revmc functions by compiling EVM bytecode, the set of instructions executed within the Ethereum Virtual Machine, into native machine code that the host system's processor can directly execute.
This process is done in two key steps:
inkwell
crate where we pass the optimized IR, and then most of our work is done. LLVM will generate the corresponding native machine code tailored to the specific architecture of the host system. This step is crucial as it determines the efficiency of the resulting executable code.The compiler is able to work either blocking or in the background. When run in a hot-path, the compiler should be run in the background to ensure that it doesn’t hurt performance of the system while it’s running, and once compilation is done it can hot-swap the interpreted execution for native execution. For benchmarking, it’s better to compile all contracts in blocking mode first and then test against your workload.
Once compiled, the native code can be stored on disk. This ensures that when the node is restarted it doesn’t spend redundant time recompiling contracts, and also allows the node operator to only run compiled contracts that they trust which have been compiled ahead of time, versus allowing any contract to be compiled at runtime.
Revmc
is integrated into Reth via the Reth SDK’s NodeBuilder API, allowing node operators to opt-into running native code via the --experimental.compiler
flag. We provide examples on how to compile bytecode into native, as well as how to integrate it inside revm’s EVM Builder. We synced the node with revmc
enabled and successfully validated the state root at the tip as of June 20th 2024.
We defined criterion benchmarks against a some simple workloads, and present our results below:
Fibonacci exhibits a 19x improvement, which is representative of computationally heavy workloads. LLVM is particularly impressive here as it auto-vectorizes instructions where it can, and leverages its own native U256 type which is faster than ruint. LLVM is unfortunately not great at optimizing divisions, so code that’s heavy in such or similar operations may not see as great benefit.
WETH and Counter are common cases of workloads we encounter in blockchains, first you read data from the host (e.g. the database), then you do some simple math, finally you write the data back into the host. Given host operations cannot be accelerated with a bytecode compiler, a 1.85x-2.77x improvement is great!
We integrated and benchmarked revmc
on Ethereum L1 via Reth’s execution stage (using reth stage run execution
), which is the dominant component of our historical sync. Because most of the historical sync’s workload on L1 is not compute-heavy, we saw less impressive results, O(1-10%) depending on the block range.
We think revmc
will truly shine on high performance L2s with computationally-heavy workloads such as Base or OP Mainnet. To prove it out, we will roll out revmc
in our upcoming Reth AlphaNet release.
Roadmap-wise, we’d like to do a few things:
Until then, check our implementation on Github. If you’re interested in working with us, reach out to georgios@paradigm.xyz
Copyright © 2024 Paradigm Operations LP All rights reserved. “Paradigm” is a trademark, and the triangular mobius symbol is a registered trademark of Paradigm Operations LP