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,75 @@ 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+ std::string case_name;
131+ if (const auto it = b.state_test .input_labels .find (case_index);
132+ it != b.state_test .input_labels .end ())
133+ {
134+ case_name = it->second ;
135+ }
136+ else
137+ case_name = std::to_string (case_index);
138+
139+
140+ for (auto & [vm_name, vm] : registered_vms)
141+ {
142+ const auto name = std::string{vm_name} + " /execute/" + b.name + ' /' + case_name;
143+
144+ const auto tx_props_or_error = state::validate_transaction (
145+ b.state_test .pre_state , block_info, tx, rev, block_info.gas_limit ,
146+ static_cast <int64_t >(state::max_blob_gas_per_block (rev)));
147+ if (const auto err = get_if<std::error_code>(&tx_props_or_error))
148+ {
149+ throw std::invalid_argument{
150+ " Transaction validation failure: " + err->message ()};
151+ }
152+
153+ const auto tx_props = get<state::TransactionProperties>(tx_props_or_error);
154+
155+ // `tx` and `tx_props` are temporary.
156+ RegisterBenchmark (name, [tx, tx_props, &vm, &b, &block_info, &rev](
157+ State& state) {
158+ bench_transition (state, vm, tx, tx_props, b.state_test .pre_state ,
159+ block_info, b.state_test .block_hashes , rev);
160+ })->Unit (kMicrosecond );
161+ }
168162 }
169163 }
170164 }
@@ -181,79 +175,35 @@ constexpr auto cli_parsing_error = -3;
181175// /
182176// / 1: evmone-bench
183177// / 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)
178+ // / 2: evmone-bench benchmarks_path
179+ // / Uses evmone VMs, loads all benchmarks from benchmarks_path. If benchmarks_path is a `json`
180+ // / file, single test is run.
181+ std::variant<int , std::vector<BenchmarkCase>> parseargs (int argc, char ** argv)
193182{
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;
183+ // Argument's placeholder:
184+ std::string benchmarks_path;
200185
201186 switch (argc)
202187 {
203188 case 1 :
204189 // Run with built-in synthetic benchmarks only.
205190 break ;
206191 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 ];
192+ benchmarks_path = argv[1 ];
217193 break ;
218194 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)};
195+ return cli_parsing_error;
243196 }
244197
245- if (!code_hex_file .empty ())
198+ if (!benchmarks_path .empty ())
246199 {
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 ()}}}}};
200+ if (fs::is_directory (benchmarks_path))
201+ return load_benchmarks_from_dir (benchmarks_path);
202+ else
203+ return load_benchmark (benchmarks_path, {});
254204 }
255205
256- return { 0 , {} };
206+ return std::vector<BenchmarkCase>{ };
257207}
258208} // namespace
259209} // namespace evmone::test
@@ -266,20 +216,28 @@ int main(int argc, char** argv)
266216 try
267217 {
268218 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 ;
219+ const auto ec_or_benchmark_cases = parseargs (argc, argv);
220+ if (std::holds_alternative<int >(ec_or_benchmark_cases))
221+ {
222+ const auto ec = std::get<int >(ec_or_benchmark_cases);
223+ if (ec == cli_parsing_error && ReportUnrecognizedArguments (argc, argv))
224+ return ec;
225+
226+ if (ec != 0 )
227+ return ec;
228+ }
229+ else
230+ {
231+ const auto benchmark_cases =
232+ std::get<std::vector<BenchmarkCase>>(ec_or_benchmark_cases);
233+ registered_vms[" advanced" ] = evmc::VM{evmc_create_evmone (), {{" advanced" , " " }}};
234+ registered_vms[" baseline" ] = evmc::VM{evmc_create_evmone ()};
235+ registered_vms[" bnocgoto" ] = evmc::VM{evmc_create_evmone (), {{" cgoto" , " no" }}};
236+ register_benchmarks (benchmark_cases);
237+ register_synthetic_benchmarks ();
238+ RunSpecifiedBenchmarks ();
239+ return 0 ;
240+ }
283241 }
284242 catch (const std::exception& ex)
285243 {
0 commit comments