Be sure to initialize all submodules
git submodule update --init --recursive
- Go toolchain, minimum version 1.19
- Ubuntu/Debian package:
golang-go
- Ubuntu/Debian package:
- C/C++ toolchain (+ standard library) supporting C++20, Clang >= 14 recommended
- Ubuntu/Debian package:
clang
- Recommended: install
clang-format
,clangd
, andgdb
for development
- Ubuntu/Debian package:
- mockgen
- Install via Go:
go install github.com/golang/mock/mockgen@v1.6.0
- Install via Go:
- CMake
- Ubuntu/Debian package:
cmake
- Ubuntu/Debian package:
Note that in some package managers (e.g. apt) the default Go package is an outdated version which is not sufficient for Tosca. Newer versions can be installed by explicitly specifying the Go version, such as golang-1.21
. Check your currently installed version with the command go version
. Depending on your installation process it might be required to set GOROOT
, this can be done in your .bashrc
.
If no packages are available, follow the instructions on the official go website to download and install the newest version.
Go installs programs into $GOPATH/bin
, where GOPATH
defaults to $HOME/go
, add this bin
directory to your PATH
.
Use the provided Makefile to build and test the project.
make
make test
Open the cpp
subdirectory in VSCode:
- Install recommended extensions: press
F1
→ Show Recommended Extensions
Select the wanted compiler (i.e. kit), build configuration and build target in VSCode's status line.
The selected target can be build via the Build button in the status line, or by pressing F7
.
Pressing the 🐜 button in VSCode's status line launches the currently selected target in the debugger.
The same can be achieved by pressing Ctrl + F5
.
To build different configurations, invoke CMake in the cpp
subdirectory:
# Debug Configuration (with AddressSanitizer)
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -DTOSCA_ASAN=ON
cmake --build build --parallel
# Release Configuration
cmake -BBuild -DCMAKE_BUILD_TYPE=Release -DTOSCA_ASAN=OFF
cmake --build build --parallel
# Run all tests
ctest --test-dir build --output-on-failure
# Run individual test
ctest --test-dir build --output-on-failure -R <test-name>
ctest
executes each unit test in isolation which is relatively slow to execute.
Alternatively, you can just run the corresponding unit test binary.
For instance:
# Run specific test binary directly
./build/vm/evmzero/uint256_test
# Build and run specific test binary directly
cmake --build build --parallel --target uint256_test && ./build/vm/evmzero/uint256_test
Note: Invoking ctest does not trigger compilation. You have to invoke the build process beforehand.
cmake --build build --parallel && ctest --test-dir build --output-on-failure
Note: For OSX, add the following argument to the initial CMake invocation:
-DCMAKE_SHARED_LIBRARY_SUFFIX_CXX=.so
Note: VSCode's multi-root workspace feature does not play nice with these extensions.
The Tosca project includes a set of benchmarks covering a range of EVM implementations and variants thereof. The benchmarks are part of the github.com/Fantom-foundation/Tosca/go/vm/test
module.
The benchmarks are implemented using Go's benchmark infrastructure. For a comprehensive introduction see the corresponding chapter in Practical Go Lessons or the Go Test Package Documentation.
To run all benchmarks, use the following command:
go test ./go/vm/test -run=NONE -bench=.
The path ./go/vm/test
points to the Go package containing the benchmarks. The flag -run=NONE
disables the execution of unit tests, and -bench=<regex>
enables the execution of benchmarks.
Optionally, to include memory usage metrics, the -benchmem
flag can be added:
go test ./go/vm/test -run=NONE -bench=. -benchmem
The command produces benchmark results in a table format:
pkg: github.com/Fantom-foundation/Tosca/go/vm/test
cpu: Intel(R) Core(TM) i7-5820K CPU @ 3.30GHz
BenchmarkInc/1/geth-12 121488 10529 ns/op 1768 B/op 16 allocs/op
BenchmarkInc/1/lfvm-12 124447 8744 ns/op 4330 B/op 17 allocs/op
BenchmarkInc/1/lfvm-si-12 140642 8472 ns/op 4330 B/op 17 allocs/op
BenchmarkInc/1/lfvm-no-sha-cache-12 132420 9165 ns/op 4330 B/op 17 allocs/op
BenchmarkInc/1/lfvm-no-code-cache-12 137041 8330 ns/op 4330 B/op 17 allocs/op
BenchmarkInc/1/evmone-12 193581 6250 ns/op 1088 B/op 15 allocs/op
BenchmarkInc/1/evmone-basic-12 186457 6330 ns/op 1088 B/op 15 allocs/op
BenchmarkInc/1/evmone-advanced-12 136111 7381 ns/op 1088 B/op 15 allocs/op
BenchmarkInc/1/evmzero-12 162552 7317 ns/op 1088 B/op 15 allocs/op
The columns are as follows:
- the name of the benchmark, using
/
as a separator between benchmarks and sub-benchmarks. In Tosca's setup, the first part of the name is the name of the benchmark (e.g.BenchmarkInc
), the second part the input size (e.g.10
), and the last part the EVM implementation (e.g.evmone-basic
) followed by the number of cores used to run the benchmark (e.g.-12
); the latter is added by Go's benchmark infrastructure and has no effect if the benchmark is not using multiple go routines. - the number of times the benchmark was executed
- the average time per execution
- the average number of bytes allocated per execution (only when using
-benchmem
) - the average number of allocations per execution (only when using
-benchmem
)
The -bench
flag can be used to filter benchmarks by their name using regex expressions. It's important to note that:
- tests are selected if the regex matches any part of the benchmark name
- before the regex expressions are applied, the pattern is devided into sub-patterns for sub-tests using the
/
separator
Thus, the command
go test ./go/vm/test -run=NONE -bench=Inc/1/zero
is running the benchmarks
BenchmarkInc/1/evmzero-12
BenchmarkInc/10/evmzero-12
since the individual parts of the benchmark name are matched one-by-one.
Go has an integrated CPU profiler that can be enabled using the -cpuprofile
flag:
go test ./go/vm/test -run=NONE -bench Fib/20/evmzero -cpuprofile cpu.log
This command runs the benchmark and collects CPU performance data which can be shown in tabular form using
go tool pprof -text -nodecount=10 cpu.log
or can be visualized by the following command (requires the graphviz libary)
go tool pprof -http "localhost:8000" ./cpu.log
which starts a web-server at port 8000
hosting an interactive set of charts to investigate the collected profiling data.
If symbols for the C++ libraries are missing, make sure the library is built including those symbols and that in some go file in the ./go/vm/test
package the following import is present:
_ "github.com/ianlancetaylor/cgosymbolizer"
This package makes C/C++ symbols accessible to the Go profiler. On some systems, however, this may hide Go symbols so the import may have to be added/removed as needed.
To compare the benchmark results of two different code versions, the benchstat tool can be utilized. To install the tool run
go install golang.org/x/perf/cmd/benchstat@latest
which places the binary benchstat
into your $GOPATH/bin
directory.
The tool produces reports on the impact of code changes on the performance of benchmarks in the following format:
name old time/op new time/op delta
Fib/20/lfvm-12 21.6ms ± 8% 21.1ms ± 2% ~ (p=0.417 n=10+7)
Fib/20/lfvm-si-12 21.7ms ± 3% 21.1ms ± 2% -2.66% (p=0.004 n=9+8)
Fib/20/lfvm-no-sha-cache-12 21.4ms ± 6% 21.0ms ± 1% ~ (p=0.165 n=10+10)
Fib/20/lfvm-no-code-cache-12 21.2ms ± 3% 20.7ms ± 3% -2.17% (p=0.006 n=9+10)
For details on how to use it, please refer to the Example section of the tool's documentation page.
Ideally, pull requests targeting performance improvements should include such a report in their description to document its impact.