Skip to content

Commit 07a5865

Browse files
committed
use output dir, use hyperfine_iteration (WIP)
Introduce a mandatory `--output-dir` option when running benchmarks. This will, in the future, be used to store benchmark configuration, results and run artifacts. Currently it will store configuration and the final run artifact, as the changes needed in hyperfine to expose the run iteration are not available yet. (see: sharkdp/hyperfine#807 )
1 parent 2e9d86b commit 07a5865

File tree

9 files changed

+117
-145
lines changed

9 files changed

+117
-145
lines changed

Diff for: benchmark.yml

-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ global:
1111
prepare: ./scripts/prepare.sh
1212
conclude: ./scripts/conclude.sh
1313
cleanup: ./scripts/cleanup.sh
14-
export_json: results.json
1514
shell: /bin/bash
1615
show_output: true
1716

@@ -50,7 +49,6 @@ benchmarks:
5049
# (bitcoin) [network] applied automatically. {dbcache} is an explicit
5150
# (additional) parameterisation from [parameter_lists] below.
5251
command: "bitcoind -dbcache={dbcache} -stopatheight=160001"
53-
prepare: echo "don't snapshot"
5452
warmup: 0
5553
runs: 1
5654

Diff for: scripts/cleanup.sh

+8-14
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,14 @@ set -e
33
echo "Running cleanup.sh"
44

55
# Scripts always recieve the same arguments from benchkit in the same order:
6-
#
7-
# pub struct ScriptArgs {
8-
# pub binary: String,
9-
# pub connect_address: String,
10-
# pub network: String,
11-
# pub snapshot_path: PathBuf,
12-
# pub tmp_data_dir: PathBuf,
13-
# }
146

15-
if [ "$#" -ne 5 ]; then
16-
echo "Error: Required arguments missing"
17-
exit 1
18-
fi
19-
20-
TMP_DATADIR="$5"
7+
# BINARY="$1"
8+
# CONNECT_ADDRESS="$2"
9+
# NETWORK="$3"
10+
# OUT_DIR="$4"
11+
# SNAPSHOT_PATH="$5"
12+
TMP_DATADIR="$6"
13+
# ITERATION="$7"
14+
# COMMIT="$8"
2115

2216
rm -Rf "${TMP_DATADIR:?}"/*

Diff for: scripts/conclude.sh

+15-19
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,25 @@ set -e
33
echo "Running conclude.sh"
44

55
# Scripts always recieve the same arguments from benchkit in the same order:
6-
#
7-
# pub struct ScriptArgs {
8-
# pub binary: String,
9-
# pub connect_address: String,
10-
# pub network: String,
11-
# pub snapshot_path: PathBuf,
12-
# pub tmp_data_dir: PathBuf,
13-
# }
14-
15-
if [ "$#" -ne 5 ]; then
16-
echo "Error: Required arguments missing"
17-
exit 1
18-
fi
196

207
# BINARY="$1"
218
# CONNECT_ADDRESS="$2"
22-
# NETWORK="$3"
23-
# SNAPSHOT_PATH="$4"
24-
TMP_DATADIR="$5"
25-
# echo "TMP_DATADIR: ${TMP_DATADIR}"
9+
NETWORK="$3"
10+
OUT_DIR="$4"
11+
# SNAPSHOT_PATH="$5"
12+
TMP_DATADIR="$6"
13+
# ITERATION="$7"
14+
COMMIT="$8"
2615

27-
# Next we move datadir files to the outdir
28-
# mv "$5"/debug.log "$2"/
16+
# Move datadir files to the outdir
17+
echo "Moving debug.log to $OUT_DIR/$COMMIT"
18+
mkdir -p "$OUT_DIR"/"$COMMIT"
19+
# TODO: include $ITERATION in this filepath
20+
if [ "$NETWORK" = "mainnet" ]; then
21+
mv "$TMP_DATADIR"/debug.log "$OUT_DIR"/"$COMMIT"/
22+
else
23+
mv "$TMP_DATADIR/$NETWORK/debug.log" "$OUT_DIR"/"$COMMIT"/
24+
fi
2925

3026
echo "Cleaning datadir contents from ${TMP_DATADIR}"
3127
rm -Rf "${TMP_DATADIR:?}"/*

Diff for: scripts/prepare.sh

+5-20
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,15 @@ set -e
33
echo "Running prepare.sh"
44

55
# Scripts always recieve the same arguments from benchkit in the same order:
6-
#
7-
# pub struct ScriptArgs {
8-
# pub binary: String,
9-
# pub connect_address: String,
10-
# pub network: String,
11-
# pub snapshot_path: PathBuf,
12-
# pub tmp_data_dir: PathBuf,
13-
# }
14-
15-
if [ "$#" -ne 5 ]; then
16-
echo "Error: Required arguments missing"
17-
exit 1
18-
fi
196

207
BINARY="$1"
218
CONNECT_ADDRESS="$2"
229
NETWORK="$3"
23-
SNAPSHOT_PATH="$4"
24-
TMP_DATADIR="$5"
25-
# echo "BINARY: ${BINARY}"
26-
# echo "CONNECT_ADDRESS: ${CONNECT_ADDRESS}"
27-
# echo "NETWORK: ${NETWORK}"
28-
# echo "SNAPSHOT_PATH: ${SNAPSHOT_PATH}"
29-
# echo "TMP_DATADIR: ${TMP_DATADIR}"
10+
# OUT_DIR="$4"
11+
SNAPSHOT_PATH="$5"
12+
TMP_DATADIR="$6"
13+
# ITERATION="$7"
14+
# COMMIT="$8"
3015

3116
mkdir -p "${TMP_DATADIR}"
3217
rm -Rf "${TMP_DATADIR:?}/*"

Diff for: scripts/setup.sh

+5-16
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,15 @@ set -e
33
echo "Running setup.sh"
44

55
# Scripts always recieve the same arguments from benchkit in the same order:
6-
#
7-
# pub struct ScriptArgs {
8-
# pub binary: String,
9-
# pub connect_address: String,
10-
# pub network: String,
11-
# pub snapshot_path: PathBuf,
12-
# pub tmp_data_dir: PathBuf,
13-
# }
14-
15-
if [ "$#" -ne 5 ]; then
16-
echo "Error: Required arguments missing"
17-
exit 1
18-
fi
196

207
# BINARY="$1"
218
# CONNECT_ADDRESS="$2"
229
# NETWORK="$3"
23-
# SNAPSHOT_PATH="$4"
24-
TMP_DATADIR="$5"
25-
# echo "TMP_DATADIR: ${TMP_DATADIR}"
10+
# OUT_DIR="$4"
11+
# SNAPSHOT_PATH="$5"
12+
TMP_DATADIR="$6"
13+
# ITERATION="$7"
14+
# COMMIT="$8"
2615

2716
echo "Creating datadir ${TMP_DATADIR} with mkdir -p"
2817
mkdir -p "${TMP_DATADIR}"

Diff for: src/benchmarks/hooks.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub struct ScriptArgs {
99
pub binary: String,
1010
pub connect_address: String,
1111
pub network: String,
12+
pub out_dir: PathBuf,
1213
pub snapshot_path: PathBuf,
1314
pub tmp_data_dir: PathBuf,
1415
}
@@ -39,13 +40,15 @@ impl HookManager {
3940
for hook_type in hook_types.iter() {
4041
if let Some(value) = options.get_mut(*hook_type) {
4142
if let Some(script) = value.as_str() {
42-
// Construct the new script command with arguments in a fixed order
43+
// Construct the new script command with arguments in a fixed order + the
44+
// hyperfine iteration counter
4345
let new_script = format!(
44-
"{} {} {} {} {} {}",
46+
"{} {} {} {} {} {} {} \"$HYPERFINE_ITERATION\" {{commit}}",
4547
script,
4648
script_args.binary,
4749
script_args.connect_address,
4850
script_args.network,
51+
script_args.out_dir.display(),
4952
script_args.snapshot_path.display(),
5053
script_args.tmp_data_dir.display(),
5154
);

Diff for: src/benchmarks/mod.rs

+53-43
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::ValueEnum;
33
use log::{debug, info};
44
use serde_json::{json, Value};
55
use std::collections::HashMap;
6-
use std::path::Path;
6+
use std::path::{Path, PathBuf};
77
use std::process::Command;
88

99
mod build;
@@ -20,36 +20,57 @@ use crate::types::Network;
2020

2121
pub struct Runner {
2222
config: GlobalConfig,
23+
out_dir: PathBuf,
2324
}
2425

2526
impl Runner {
26-
pub fn new(config: GlobalConfig) -> Result<Self> {
27-
Ok(Self { config })
27+
pub fn new(
28+
config: GlobalConfig,
29+
out_dir: PathBuf,
30+
app_config: &PathBuf,
31+
bench_config: &PathBuf,
32+
) -> Result<Self> {
33+
// Configure stage
34+
debug!("Using output directory: {}", out_dir.display());
35+
std::fs::create_dir_all(&out_dir)?;
36+
if std::fs::read_dir(&out_dir)?.next().is_some() {
37+
anyhow::bail!(
38+
"Output directory '{}' is not empty. Please clear it before running benchmarks",
39+
out_dir.display()
40+
);
41+
}
42+
let app_config_name = app_config.file_name().unwrap_or_default();
43+
let bench_config_name = bench_config.file_name().unwrap_or_default();
44+
std::fs::copy(app_config, out_dir.join(app_config_name))?;
45+
std::fs::copy(bench_config, out_dir.join(bench_config_name))?;
46+
47+
Ok(Self { config, out_dir })
2848
}
2949

30-
pub async fn run(&self) -> Result<()> {
31-
for bench in &self.config.bench.benchmarks {
50+
pub async fn run(&self, name: Option<&str>) -> Result<()> {
51+
let benchmarks = match name {
52+
Some(n) => {
53+
let bench = self
54+
.config
55+
.bench
56+
.benchmarks
57+
.iter()
58+
.find(|b| b.name == n)
59+
.with_context(|| format!("Benchmark not found: {}", n))?;
60+
vec![bench]
61+
}
62+
None => self.config.bench.benchmarks.iter().collect(),
63+
};
64+
65+
for bench in benchmarks {
66+
// TODO: Remove this check to enable runs without AssumeUTXO
3267
self.check_snapshot(bench, &self.config.app.snapshot_dir)
3368
.await?;
3469
self.run_benchmark(bench).await?;
3570
}
3671
Ok(())
3772
}
3873

39-
pub async fn run_single(&self, name: &str) -> Result<()> {
40-
let bench = self
41-
.config
42-
.bench
43-
.benchmarks
44-
.iter()
45-
.find(|b| b.name == name)
46-
.with_context(|| format!("Benchmark not found: {}", name))?;
47-
48-
self.check_snapshot(bench, &self.config.app.snapshot_dir)
49-
.await?;
50-
self.run_benchmark(bench).await
51-
}
52-
5374
async fn check_snapshot(&self, bench: &SingleConfig, snapshot_dir: &Path) -> Result<()> {
5475
// Check if we have the correct snapshot
5576
let network = Network::from_str(&bench.network, true)
@@ -82,9 +103,6 @@ This can be downloaded with `benchkit snapshot download {}`",
82103
}
83104
merged_hyperfine.extend(bench.hyperfine.clone());
84105

85-
// Create a temporary output directory
86-
// let out_dir = tempfile::TempDir::new()?.into_path();
87-
88106
// Update command to use full binary path and apply chain= param
89107
if let Some(Value::String(command)) = merged_hyperfine.get_mut("command") {
90108
let new_command = command.replace(
@@ -116,6 +134,7 @@ This can be downloaded with `benchkit snapshot download {}`",
116134
let script_args = ScriptArgs {
117135
binary: format!("{}/bitcoind-{{commit}}", self.config.app.bin_dir.display()),
118136
connect_address: bench.connect.clone().unwrap_or_default(),
137+
out_dir: self.out_dir.clone(),
119138
network: bench.network.clone(),
120139
snapshot_path,
121140
tmp_data_dir: self.config.bench.global.tmp_data_dir.clone(),
@@ -127,11 +146,11 @@ This can be downloaded with `benchkit snapshot download {}`",
127146
.with_context(|| "Failed to add hyperfine script hooks")?;
128147

129148
// Add commits to parameter-lists if not already present
130-
let param_lists = merged_hyperfine
149+
let parameter_lists = merged_hyperfine
131150
.entry("parameter_lists".to_string())
132151
.or_insert_with(|| Value::Array(Vec::new()));
133152

134-
if let Value::Array(lists) = param_lists {
153+
if let Value::Array(lists) = parameter_lists {
135154
// Check if commits parameter list already exists
136155
if !lists
137156
.iter()
@@ -146,31 +165,27 @@ This can be downloaded with `benchkit snapshot download {}`",
146165
}
147166
}
148167

149-
// Check the export path before running hyperfine
150-
let export_path = merged_hyperfine
151-
.get("export_json")
152-
.and_then(Value::as_str)
153-
.with_context(|| {
154-
format!(
155-
"Missing required 'export_json' field in benchmark '{}'",
156-
bench.name
157-
)
158-
})?;
168+
// Hardcode the export path to the top-level of the out_dir
169+
let export_path = self.out_dir.join("results.json");
170+
merged_hyperfine.insert(
171+
"export_json".to_string(),
172+
Value::String(export_path.to_string_lossy().into_owned()),
173+
);
159174

160175
// Run hyperfine with merged options
161176
self.run_hyperfine(bench, &merged_hyperfine)?;
162177

163178
// Check for and process results
164-
if !Path::new(export_path).exists() {
179+
if !Path::new(&export_path).exists() {
165180
anyhow::bail!(
166181
"Expected JSON results file not found at '{}' for benchmark '{}'",
167-
export_path,
182+
export_path.display(),
168183
bench.name
169184
);
170185
}
171186

172-
let results_json = std::fs::read_to_string(export_path)
173-
.with_context(|| format!("Failed to read results file: {}", export_path))?;
187+
let results_json = std::fs::read_to_string(&export_path)
188+
.with_context(|| format!("Failed to read results file: {}", export_path.display()))?;
174189

175190
// Store results in database
176191
crate::database::store_results(
@@ -180,11 +195,6 @@ This can be downloaded with `benchkit snapshot download {}`",
180195
self.config.bench.run_id,
181196
)
182197
.await?;
183-
184-
// Cleanup
185-
std::fs::remove_file(export_path)
186-
.with_context(|| format!("Failed to remove results file: {}", export_path))?;
187-
188198
Ok(())
189199
}
190200

Diff for: src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ pub fn load_app_config(app_config_path: &PathBuf) -> Result<AppConfig> {
5252

5353
// Resolve relative paths to absolute paths and create directories
5454
for path in [
55-
&mut config.home_dir,
5655
&mut config.bin_dir,
56+
&mut config.home_dir,
5757
&mut config.snapshot_dir,
5858
]
5959
.iter_mut()

0 commit comments

Comments
 (0)