22// Copyright 2019 The evmone Authors.
33// SPDX-License-Identifier: Apache-2.0
44
5- #include " ../statetest/statetest.hpp"
65#include " helpers.hpp"
76#include " synthetic_benchmarks.hpp"
87#include < benchmark/benchmark.h>
98#include < evmc/evmc.hpp>
10- #include < evmc/loader.h>
119#include < evmone/evmone.h>
10+ #include < gtest/gtest.h>
11+ #include < test/statetest/statetest.hpp>
1212#include < filesystem>
1313#include < fstream>
1414#include < iostream>
@@ -26,70 +26,55 @@ namespace
2626{
2727struct BenchmarkCase
2828{
29- struct Input
30- {
31- std::string name;
32- bytes input;
33- bytes expected_output;
34-
35- Input (std::string _name, bytes _input, bytes _expected_output = {}) noexcept
36- : name{std::move (_name)},
37- input{std::move (_input)},
38- expected_output{std::move (_expected_output)}
39- {}
40- };
41-
29+ StateTransitionTest state_test;
4230 std::string name;
43- bytes code;
44- std::vector<Input> inputs;
4531};
4632
47- // / Loads the benchmark case's inputs from the inputs file at the given path.
48- std::vector<BenchmarkCase::Input> load_inputs (const StateTransitionTest& state_test)
49- {
50- std::vector<BenchmarkCase::Input> inputs;
51- inputs.reserve (state_test.multi_tx .inputs .size ());
52- for (size_t i = 0 ; i < state_test.multi_tx .inputs .size (); ++i)
53- inputs.emplace_back (state_test.input_labels .at (i), state_test.multi_tx .inputs [i]);
54- return inputs;
55- }
56-
5733// / Loads a benchmark case from a file at `path` and all its inputs from the matching inputs file.
58- BenchmarkCase load_benchmark (const fs::path& path, const std::string& name_prefix)
34+ std::vector< BenchmarkCase> load_benchmark (const fs::path& path, const std::string& name_prefix)
5935{
6036 std::ifstream f{path};
61- auto state_test = std::move ( load_state_tests (f). at ( 0 )) ;
37+ std::vector<BenchmarkCase> result ;
6238
63- const auto name = name_prefix + path.stem ().string ();
64- const auto code = state_test.pre_state .get (state_test.multi_tx .to .value ()).code ;
65- const auto inputs = load_inputs (state_test);
39+ auto state_tests = load_state_tests (f);
40+ result.reserve (state_tests.size ());
41+ for (const auto & state_test : state_tests)
42+ {
43+ // If a file has a single test, skip its name.
44+ auto name = name_prefix + path.stem ().string () +
45+ (state_tests.size () > 1 ? " /" + state_test.name : " " );
46+ result.emplace_back (BenchmarkCase{state_test, std::move (name)});
47+ }
6648
67- return BenchmarkCase{name, code, inputs} ;
49+ return result ;
6850}
6951
7052// / Loads all benchmark cases from the given directory and all its subdirectories.
7153std::vector<BenchmarkCase> load_benchmarks_from_dir ( // NOLINT(misc-no-recursion)
7254 const fs::path& path, const std::string& name_prefix = {})
7355{
7456 std::vector<fs::path> subdirs;
75- std::vector<fs::path> code_files ;
57+ std::vector<fs::path> files ;
7658
7759 for (auto & e : fs::directory_iterator{path})
7860 {
7961 if (e.is_directory ())
8062 subdirs.emplace_back (e);
8163 else if (e.path ().extension () == " .json" )
82- code_files .emplace_back (e);
64+ files .emplace_back (e);
8365 }
8466
8567 std::ranges::sort (subdirs);
86- std::ranges::sort (code_files );
68+ std::ranges::sort (files );
8769
8870 std::vector<BenchmarkCase> benchmark_cases;
8971
90- benchmark_cases.reserve (std::size (code_files));
91- for (const auto & f : code_files)
92- benchmark_cases.emplace_back (load_benchmark (f, name_prefix));
72+ for (const auto & f : files)
73+ {
74+ auto t = load_benchmark (f, name_prefix);
75+ benchmark_cases.insert (benchmark_cases.end (), std::make_move_iterator (t.begin ()),
76+ std::make_move_iterator (t.end ()));
77+ }
9378
9479 for (const auto & d : subdirs)
9580 {
@@ -105,66 +90,66 @@ void register_benchmarks(std::span<const BenchmarkCase> benchmark_cases)
10590{
10691 evmc::VM* advanced_vm = nullptr ;
10792 evmc::VM* baseline_vm = nullptr ;
108- evmc::VM* basel_cg_vm = nullptr ;
10993 if (const auto it = registered_vms.find (" advanced" ); it != registered_vms.end ())
11094 advanced_vm = &it->second ;
11195 if (const auto it = registered_vms.find (" baseline" ); it != registered_vms.end ())
11296 baseline_vm = &it->second ;
113- if ( const auto it = registered_vms. find ( " bnocgoto " ); it != registered_vms. end ())
114- basel_cg_vm = &it-> second ;
97+
98+ evmc::VM check_test_vm = evmc::VM{ evmc_create_evmone ()} ;
11599
116100 for (const auto & b : benchmark_cases)
117101 {
118- if (advanced_vm != nullptr )
119- {
120- RegisterBenchmark (" advanced/analyse/" + b.name , [&b](State& state) {
121- bench_analyse<advanced::AdvancedCodeAnalysis, advanced_analyse>(
122- state, default_revision, b.code );
123- })->Unit (kMicrosecond );
124- }
102+ run_state_test (b.state_test , check_test_vm, false );
125103
126- if (baseline_vm != nullptr )
127- {
128- RegisterBenchmark (" baseline/analyse/" + b.name , [&b](State& state) {
129- bench_analyse<baseline::CodeAnalysis, baseline_analyse>(
130- state, default_revision, b.code );
131- })->Unit (kMicrosecond );
132- }
104+ if (::testing::Test::HasFailure ())
105+ throw std::invalid_argument{" State test you want to bench failed." };
133106
134- for (const auto & input : b.inputs )
107+ const auto code = b.state_test .pre_state .get_account_code (b.state_test .multi_tx .to .value ());
108+ for (const auto & [rev, cases, block_info] : b.state_test .cases )
135109 {
136- const auto case_name = b.name + (!input.name .empty () ? ' /' + input.name : " " );
137-
138110 if (advanced_vm != nullptr )
139111 {
140- const auto name = " advanced/execute /" + case_name;
141- RegisterBenchmark (name, [&vm = *advanced_vm, &b, &input](State& state) {
142- bench_advanced_execute (state, vm, b. code , input. input , input. expected_output );
112+ RegisterBenchmark ( " advanced/analyse /" + b. name , [code, &rev](State& state) {
113+ bench_analyse<advanced::AdvancedCodeAnalysis, advanced_analyse>(
114+ state, rev, code );
143115 })->Unit (kMicrosecond );
144116 }
145117
146118 if (baseline_vm != nullptr )
147119 {
148- const auto name = " baseline/execute/" + case_name;
149- RegisterBenchmark (name, [&vm = *baseline_vm, &b, &input](State& state) {
150- bench_baseline_execute (state, vm, b.code , input.input , input.expected_output );
151- })->Unit (kMicrosecond );
152- }
153-
154- if (basel_cg_vm != nullptr )
155- {
156- const auto name = " bnocgoto/execute/" + case_name;
157- RegisterBenchmark (name, [&vm = *basel_cg_vm, &b, &input](State& state) {
158- bench_baseline_execute (state, vm, b.code , input.input , input.expected_output );
120+ RegisterBenchmark (" baseline/analyse/" + b.name , [code, &rev](State& state) {
121+ bench_analyse<baseline::CodeAnalysis, baseline_analyse>(state, rev, code);
159122 })->Unit (kMicrosecond );
160123 }
161124
162- for (auto & [vm_name, vm] : registered_vms )
125+ for (size_t case_index = 0 ; case_index != cases. size (); ++case_index )
163126 {
164- const auto name = std::string{vm_name} + " /total/" + case_name;
165- RegisterBenchmark (name, [&vm, &b, &input](State& state) {
166- bench_evmc_execute (state, vm, b.code , input.input , input.expected_output );
167- })->Unit (kMicrosecond );
127+ const auto & expected = cases[case_index];
128+ const auto tx = b.state_test .multi_tx .get (expected.indexes );
129+
130+ for (auto & [vm_name, vm] : registered_vms)
131+ {
132+ const auto name = std::string{vm_name} + " /execute/" + b.name + ' /' +
133+ std::to_string (case_index);
134+
135+ const auto tx_props_or_error = state::validate_transaction (
136+ b.state_test .pre_state , block_info, tx, rev, block_info.gas_limit ,
137+ static_cast <int64_t >(state::max_blob_gas_per_block (rev)));
138+ if (const auto err = get_if<std::error_code>(&tx_props_or_error))
139+ {
140+ throw std::invalid_argument{
141+ " Transaction validation failure: " + err->message ()};
142+ }
143+
144+ const auto tx_props = get<state::TransactionProperties>(tx_props_or_error);
145+
146+ // `tx` and `tx_props` are temporary.
147+ RegisterBenchmark (name, [tx, tx_props, &vm, &b, &block_info, &rev](
148+ State& state) {
149+ bench_transition (state, vm, tx, tx_props, b.state_test .pre_state ,
150+ block_info, b.state_test .block_hashes , rev);
151+ })->Unit (kMicrosecond );
152+ }
168153 }
169154 }
170155 }
@@ -181,79 +166,35 @@ constexpr auto cli_parsing_error = -3;
181166// /
182167// / 1: evmone-bench
183168// / Uses evmone VMs, only synthetic benchmarks are available.
184- // / 2: evmone-bench benchmarks_dir
185- // / Uses evmone VMs, loads all benchmarks from benchmarks_dir.
186- // / 3: evmone-bench evmc_config benchmarks_dir
187- // / The same as (2) but loads additional custom EVMC VM.
188- // / 4: evmone-bench code_hex_file input_hex expected_output_hex.
189- // / Uses evmone VMs, registers custom benchmark with the code from the given file,
190- // / and the given input. The benchmark will compare the output with the provided
191- // / expected one.
192- std::tuple<int , std::vector<BenchmarkCase>> parseargs (int argc, char ** argv)
169+ // / 2: evmone-bench benchmarks_path
170+ // / Uses evmone VMs, loads all benchmarks from benchmarks_path. If benchmarks_path is a `json`
171+ // / file, single test is run.
172+ std::variant<int , std::vector<BenchmarkCase>> parseargs (int argc, char ** argv)
193173{
194- // Arguments' placeholders:
195- std::string evmc_config;
196- std::string benchmarks_dir;
197- std::string code_hex_file;
198- std::string input_hex;
199- std::string expected_output_hex;
174+ // Argument's placeholder:
175+ std::string benchmarks_path;
200176
201177 switch (argc)
202178 {
203179 case 1 :
204180 // Run with built-in synthetic benchmarks only.
205181 break ;
206182 case 2 :
207- benchmarks_dir = argv[1 ];
208- break ;
209- case 3 :
210- evmc_config = argv[1 ];
211- benchmarks_dir = argv[2 ];
212- break ;
213- case 4 :
214- code_hex_file = argv[1 ];
215- input_hex = argv[2 ];
216- expected_output_hex = argv[3 ];
183+ benchmarks_path = argv[1 ];
217184 break ;
218185 default :
219- std::cerr << " Too many arguments\n " ;
220- return {cli_parsing_error, {}};
221- }
222-
223- if (!evmc_config.empty ())
224- {
225- auto ec = evmc_loader_error_code{};
226- registered_vms[" external" ] = evmc::VM{evmc_load_and_configure (evmc_config.c_str (), &ec)};
227-
228- if (ec != EVMC_LOADER_SUCCESS)
229- {
230- if (const auto error = evmc_last_error_msg ())
231- std::cerr << " EVMC loading error: " << error << " \n " ;
232- else
233- std::cerr << " EVMC loading error " << ec << " \n " ;
234- return {static_cast <int >(ec), {}};
235- }
236-
237- std::cout << " External VM: " << evmc_config << " \n " ;
238- }
239-
240- if (!benchmarks_dir.empty ())
241- {
242- return {0 , load_benchmarks_from_dir (benchmarks_dir)};
186+ return cli_parsing_error;
243187 }
244188
245- if (!code_hex_file .empty ())
189+ if (!benchmarks_path .empty ())
246190 {
247- std::ifstream file{code_hex_file};
248- return {0 , {BenchmarkCase{code_hex_file,
249- from_spaced_hex (
250- std::istreambuf_iterator<char >{file}, std::istreambuf_iterator<char >{})
251- .value (),
252- {BenchmarkCase::Input{" " , from_hex (input_hex).value (),
253- from_hex (expected_output_hex).value ()}}}}};
191+ if (fs::is_directory (benchmarks_path))
192+ return load_benchmarks_from_dir (benchmarks_path);
193+ else
194+ return load_benchmark (benchmarks_path, {});
254195 }
255196
256- return { 0 , {} };
197+ return std::vector<BenchmarkCase>{ };
257198}
258199} // namespace
259200} // namespace evmone::test
@@ -266,20 +207,28 @@ int main(int argc, char** argv)
266207 try
267208 {
268209 Initialize (&argc, argv); // Consumes --benchmark_ options.
269- const auto [ec, benchmark_cases] = parseargs (argc, argv);
270- if (ec == cli_parsing_error && ReportUnrecognizedArguments (argc, argv))
271- return ec;
272-
273- if (ec != 0 )
274- return ec;
275-
276- registered_vms[" advanced" ] = evmc::VM{evmc_create_evmone (), {{" advanced" , " " }}};
277- registered_vms[" baseline" ] = evmc::VM{evmc_create_evmone ()};
278- registered_vms[" bnocgoto" ] = evmc::VM{evmc_create_evmone (), {{" cgoto" , " no" }}};
279- register_benchmarks (benchmark_cases);
280- register_synthetic_benchmarks ();
281- RunSpecifiedBenchmarks ();
282- return 0 ;
210+ const auto ec_or_benchmark_cases = parseargs (argc, argv);
211+ if (std::holds_alternative<int >(ec_or_benchmark_cases))
212+ {
213+ const auto ec = std::get<int >(ec_or_benchmark_cases);
214+ if (ec == cli_parsing_error && ReportUnrecognizedArguments (argc, argv))
215+ return ec;
216+
217+ if (ec != 0 )
218+ return ec;
219+ }
220+ else
221+ {
222+ const auto benchmark_cases =
223+ std::get<std::vector<BenchmarkCase>>(ec_or_benchmark_cases);
224+ registered_vms[" advanced" ] = evmc::VM{evmc_create_evmone (), {{" advanced" , " " }}};
225+ registered_vms[" baseline" ] = evmc::VM{evmc_create_evmone ()};
226+ registered_vms[" bnocgoto" ] = evmc::VM{evmc_create_evmone (), {{" cgoto" , " no" }}};
227+ register_benchmarks (benchmark_cases);
228+ register_synthetic_benchmarks ();
229+ RunSpecifiedBenchmarks ();
230+ return 0 ;
231+ }
283232 }
284233 catch (const std::exception& ex)
285234 {
0 commit comments