Skip to content

Commit 82f4595

Browse files
Assert wasm and v8 fuel usage in keynote bench test
1 parent 1fa49c1 commit 82f4595

3 files changed

Lines changed: 109 additions & 4 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/ci/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ anyhow.workspace = true
99
chrono = { workspace = true, features=["clock"] }
1010
clap.workspace = true
1111
regex.workspace = true
12+
reqwest = { workspace = true, features = ["blocking"] }
1213
serde_json.workspace = true
1314
duct.workspace = true
1415
tempfile.workspace = true

tools/ci/src/keynote_bench.rs

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ const BENCH_CONCURRENCY: &str = "64";
1717
const MAX_INFLIGHT_PER_WORKER: &str = "96";
1818
const SEED_ACCOUNTS: &str = "100000";
1919
const SEED_INITIAL_BALANCE: &str = "1000000000000";
20+
const TRANSFER_REDUCER: &str = "transfer";
21+
const REDUCER_FUEL_METRIC: &str = "reducer_wasmtime_fuel_used";
22+
const REDUCER_FUEL_METRIC_PREFIX: &str = "reducer_wasmtime_fuel_used{";
23+
const REDUCER_FUEL_METRIC_TOTAL_PREFIX: &str = "reducer_wasmtime_fuel_used_total{";
24+
const MAX_FUEL_RATIO: f64 = 10.0;
2025

2126
struct BenchmarkModule {
2227
label: &'static str,
2328
module_dir: &'static str,
2429
min_tps: f64,
2530
}
2631

32+
struct BenchmarkResult {
33+
label: &'static str,
34+
transfer_fuel_total: f64,
35+
}
36+
2737
const BENCHMARK_MODULES: &[BenchmarkModule] = &[
2838
BenchmarkModule {
2939
label: "TypeScript",
@@ -43,14 +53,26 @@ pub fn run() -> Result<()> {
4353
let cli_config_dir = tempfile::tempdir().context("failed to create temporary CLI config directory")?;
4454
let cli_config_path = cli_config_dir.path().join("config.toml");
4555

56+
let mut results = Vec::with_capacity(BENCHMARK_MODULES.len());
4657
for module in BENCHMARK_MODULES {
47-
run_module_benchmark(module, &cli_path, &cli_config_path, &server.host_url)?;
58+
results.push(run_module_benchmark(
59+
module,
60+
&cli_path,
61+
&cli_config_path,
62+
&server.host_url,
63+
)?);
4864
}
65+
check_transfer_fuel_ratio(&results)?;
4966

5067
Ok(())
5168
}
5269

53-
fn run_module_benchmark(module: &BenchmarkModule, cli_path: &Path, config_path: &Path, server_url: &str) -> Result<()> {
70+
fn run_module_benchmark(
71+
module: &BenchmarkModule,
72+
cli_path: &Path,
73+
config_path: &Path,
74+
server_url: &str,
75+
) -> Result<BenchmarkResult> {
5476
eprintln!(
5577
"Running keynote benchmark against {} module ({})...",
5678
module.label, module.module_dir
@@ -60,7 +82,15 @@ fn run_module_benchmark(module: &BenchmarkModule, cli_path: &Path, config_path:
6082
generate_module_bindings(module, cli_path, config_path)?;
6183
seed_accounts(cli_path, config_path, server_url)?;
6284
let runs_dir = tempfile::tempdir().context("failed to create temporary benchmark runs directory")?;
85+
let transfer_fuel_before = transfer_fuel_total(server_url)?;
6386
run_benchmark(module, server_url, runs_dir.path())?;
87+
let transfer_fuel_after = transfer_fuel_total(server_url)?;
88+
let transfer_fuel_total = transfer_fuel_after - transfer_fuel_before;
89+
ensure!(
90+
transfer_fuel_total > 0.0,
91+
"{} keynote benchmark did not record any fuel for the {TRANSFER_REDUCER} reducer",
92+
module.label
93+
);
6494

6595
let result_path = find_result_json(runs_dir.path())?;
6696
let result_json = fs::read_to_string(&result_path)
@@ -79,12 +109,16 @@ fn run_module_benchmark(module: &BenchmarkModule, cli_path: &Path, config_path:
79109
}
80110

81111
println!(
82-
"Keynote perf check passed for {} module: throughput {tps:.0} TPS >= {:.0} TPS ({})",
112+
"Keynote perf check passed for {} module: throughput {tps:.0} TPS >= {:.0} TPS; \
113+
{TRANSFER_REDUCER} fuel total {transfer_fuel_total:.0} ({})",
83114
module.label,
84115
module.min_tps,
85116
result_path.display()
86117
);
87-
Ok(())
118+
Ok(BenchmarkResult {
119+
label: module.label,
120+
transfer_fuel_total,
121+
})
88122
}
89123

90124
fn publish_module(module: &BenchmarkModule, cli_path: &Path, config_path: &Path, server_url: &str) -> Result<()> {
@@ -214,3 +248,72 @@ fn result_tps(result_json: &str) -> Result<f64> {
214248
.and_then(Value::as_f64)
215249
.context("benchmark result JSON is missing results[0].res.tps")
216250
}
251+
252+
fn transfer_fuel_total(server_url: &str) -> Result<f64> {
253+
let metrics_url = format!("{}/v1/metrics", server_url.trim_end_matches('/'));
254+
let metrics = reqwest::blocking::get(&metrics_url)
255+
.with_context(|| format!("failed to fetch metrics from {metrics_url}"))?
256+
.error_for_status()
257+
.with_context(|| format!("metrics request to {metrics_url} failed"))?
258+
.text()
259+
.context("failed to read metrics response body")?;
260+
261+
let transfer_label = format!(r#"reducer="{TRANSFER_REDUCER}""#);
262+
let mut total = 0.0;
263+
for line in metrics.lines() {
264+
if !is_reducer_fuel_metric_line(line) || !line.contains(&transfer_label) {
265+
continue;
266+
}
267+
let value = line
268+
.split_whitespace()
269+
.nth(1)
270+
.with_context(|| format!("malformed {REDUCER_FUEL_METRIC} metric line: {line}"))?
271+
.parse::<f64>()
272+
.with_context(|| format!("invalid {REDUCER_FUEL_METRIC} metric value in line: {line}"))?;
273+
total += value;
274+
}
275+
Ok(total)
276+
}
277+
278+
fn is_reducer_fuel_metric_line(line: &str) -> bool {
279+
line.starts_with(REDUCER_FUEL_METRIC_PREFIX) || line.starts_with(REDUCER_FUEL_METRIC_TOTAL_PREFIX)
280+
}
281+
282+
fn check_transfer_fuel_ratio(results: &[BenchmarkResult]) -> Result<()> {
283+
ensure!(
284+
results.len() == 2,
285+
"expected exactly two keynote benchmark results to compare fuel usage, got {}",
286+
results.len()
287+
);
288+
let [first, second] = results else {
289+
unreachable!("length was checked above")
290+
};
291+
292+
let higher = first.transfer_fuel_total.max(second.transfer_fuel_total);
293+
let lower = first.transfer_fuel_total.min(second.transfer_fuel_total);
294+
ensure!(
295+
lower > 0.0,
296+
"keynote benchmark fuel totals must be nonzero: {}={:.0}, {}={:.0}",
297+
first.label,
298+
first.transfer_fuel_total,
299+
second.label,
300+
second.transfer_fuel_total
301+
);
302+
303+
let ratio = higher / lower;
304+
println!(
305+
"Keynote transfer fuel comparison: {}={:.0}, {}={:.0}, relative difference {ratio:.2}x",
306+
first.label, first.transfer_fuel_total, second.label, second.transfer_fuel_total
307+
);
308+
ensure!(
309+
ratio < MAX_FUEL_RATIO,
310+
"keynote benchmark transfer fuel totals differ by {ratio:.2}x, expected strictly less than {MAX_FUEL_RATIO}x: \
311+
{}={:.0}, {}={:.0}",
312+
first.label,
313+
first.transfer_fuel_total,
314+
second.label,
315+
second.transfer_fuel_total
316+
);
317+
318+
Ok(())
319+
}

0 commit comments

Comments
 (0)