Skip to content

Commit 6132839

Browse files
committed
test: Handle full state tests in evmone-becnh
- Load benchmarks as proper state test. - Support single file path. - Remove support for benchmarking raw bytecode.
1 parent 7e4eb65 commit 6132839

File tree

2 files changed

+119
-173
lines changed

2 files changed

+119
-173
lines changed

test/bench/bench.cpp

Lines changed: 99 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
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
{
2727
struct 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.
7153
std::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

Comments
 (0)