Skip to content

Commit 80844ab

Browse files
committed
make random access timed instead of set iterations
Signed-off-by: Connor Tsui <[email protected]>
1 parent 7017027 commit 80844ab

File tree

3 files changed

+116
-44
lines changed

3 files changed

+116
-44
lines changed

bench-vortex/src/bench_run.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,42 @@ where
4141

4242
fastest_result
4343
}
44+
45+
/// Run a benchmark for a specified time limit, collecting all run durations.
46+
///
47+
/// At least one run is always guaranteed, even if it exceeds the time limit.
48+
pub fn run_timed_with_setup<I, O, S, R, F>(
49+
runtime: &Runtime,
50+
time_limit_secs: u64,
51+
mut setup: S,
52+
mut routine: R,
53+
) -> Vec<Duration>
54+
where
55+
S: FnMut() -> I,
56+
R: FnMut(I) -> F,
57+
F: Future<Output = O>,
58+
{
59+
let time_limit = Duration::from_secs(time_limit_secs);
60+
let overall_start = Instant::now();
61+
let mut runs = Vec::new();
62+
63+
// Looping like this ensures at least one run happens.
64+
loop {
65+
let state = black_box(setup());
66+
let elapsed = runtime.block_on(async {
67+
let start = Instant::now();
68+
let output = routine(state).await;
69+
let elapsed = start.elapsed();
70+
drop(black_box(output));
71+
elapsed
72+
});
73+
runs.push(elapsed);
74+
75+
// Check if we should continue.
76+
if overall_start.elapsed() >= time_limit {
77+
break;
78+
}
79+
}
80+
81+
runs
82+
}

bench-vortex/src/bin/random_access.rs

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
use std::fs::File;
5+
use std::future::Future;
56
use std::io::{Write, stdout};
67
use std::path::PathBuf;
78

8-
use bench_vortex::bench_run::run_with_setup;
9+
use bench_vortex::bench_run::run_timed_with_setup;
910
use bench_vortex::datasets::taxi_data::*;
1011
use bench_vortex::display::{DisplayFormat, print_measurements_json, render_table};
1112
use bench_vortex::measurements::TimingMeasurement;
@@ -34,8 +35,9 @@ struct Args {
3435
default_values_t = vec![Format::Parquet, Format::OnDiskVortex]
3536
)]
3637
formats: Vec<Format>,
37-
#[arg(short, long, default_value_t = 10)]
38-
iterations: usize,
38+
/// Time limit in seconds for each benchmark target (e.g., 10 for 10 seconds).
39+
#[arg(long, default_value_t = 10)]
40+
time_limit: u64,
3941
#[arg(short, long)]
4042
threads: Option<usize>,
4143
#[arg(short, long)]
@@ -61,41 +63,48 @@ fn main() -> anyhow::Result<()> {
6163
random_access(
6264
args.formats,
6365
runtime,
64-
args.iterations,
66+
args.time_limit,
6567
args.display_format,
6668
indices,
6769
&args.output_path,
6870
)
6971
}
7072

71-
/// Given a benchmark future, runs it and returns a [`TimingMeasurement`].
72-
fn create_timing_measurement<O, B, F>(
73-
benchmark: B,
73+
/// Configuration for timing measurements
74+
struct TimingConfig<'a> {
7475
name: String,
7576
storage: String,
76-
runtime: &Runtime,
77-
indices: &Buffer<u64>,
78-
iterations: usize,
77+
runtime: &'a Runtime,
78+
indices: &'a Buffer<u64>,
79+
time_limit: u64,
7980
target: Target,
80-
) -> TimingMeasurement
81+
}
82+
83+
/// Given a benchmark future, runs it and returns a [`TimingMeasurement`].
84+
fn create_timing_measurement<O, B, F>(benchmark: B, config: TimingConfig) -> TimingMeasurement
8185
where
8286
B: FnMut(Buffer<u64>) -> F,
8387
F: Future<Output = O>,
8488
{
85-
let benchmark_duration = run_with_setup(runtime, iterations, || indices.clone(), benchmark);
89+
let runs = run_timed_with_setup(
90+
config.runtime,
91+
config.time_limit,
92+
|| config.indices.clone(),
93+
benchmark,
94+
);
8695

8796
TimingMeasurement {
88-
name,
89-
storage,
90-
target,
91-
time: benchmark_duration,
97+
name: config.name,
98+
storage: config.storage,
99+
target: config.target,
100+
runs,
92101
}
93102
}
94103

95104
fn random_access(
96105
formats: Vec<Format>,
97106
runtime: Runtime,
98-
iterations: usize,
107+
time_limit: u64,
99108
display_format: DisplayFormat,
100109
indices: Buffer<u64>,
101110
output_path: &Option<PathBuf>,
@@ -123,12 +132,14 @@ fn random_access(
123132
|indices| async {
124133
take_vortex_tokio(&taxi_vortex, indices, validate_vortex_array).await
125134
},
126-
"random-access/vortex-tokio-local-disk".to_string(),
127-
STORAGE_NVME.to_owned(),
128-
&runtime,
129-
&indices,
130-
iterations,
131-
target,
135+
TimingConfig {
136+
name: "random-access/vortex-tokio-local-disk".to_string(),
137+
storage: STORAGE_NVME.to_owned(),
138+
runtime: &runtime,
139+
indices: &indices,
140+
time_limit,
141+
target,
142+
},
132143
)
133144
}
134145
Format::VortexCompact => {
@@ -139,25 +150,29 @@ fn random_access(
139150
take_vortex_tokio(&taxi_vortex_compact, indices, validate_vortex_array)
140151
.await
141152
},
142-
"random-access/vortex-compact-tokio-local-disk".to_string(),
143-
STORAGE_NVME.to_owned(),
144-
&runtime,
145-
&indices,
146-
iterations,
147-
target,
153+
TimingConfig {
154+
name: "random-access/vortex-compact-tokio-local-disk".to_string(),
155+
storage: STORAGE_NVME.to_owned(),
156+
runtime: &runtime,
157+
indices: &indices,
158+
time_limit,
159+
target,
160+
},
148161
)
149162
}
150163
Format::Parquet => {
151164
let taxi_parquet = runtime.block_on(taxi_data_parquet())?;
152165

153166
create_timing_measurement(
154167
|indices| async { take_parquet(&taxi_parquet, indices).await },
155-
"random-access/parquet-tokio-local-disk".to_string(),
156-
STORAGE_NVME.to_owned(),
157-
&runtime,
158-
&indices,
159-
iterations,
160-
target,
168+
TimingConfig {
169+
name: "random-access/parquet-tokio-local-disk".to_string(),
170+
storage: STORAGE_NVME.to_owned(),
171+
runtime: &runtime,
172+
indices: &indices,
173+
time_limit,
174+
target,
175+
},
161176
)
162177
}
163178
#[cfg(feature = "lance")]
@@ -166,12 +181,14 @@ fn random_access(
166181

167182
create_timing_measurement(
168183
|indices| async { take_lance(&taxi_lance, indices).await },
169-
"random-access/lance-tokio-local-disk".to_string(),
170-
STORAGE_NVME.to_owned(),
171-
&runtime,
172-
&indices,
173-
iterations,
174-
target,
184+
TimingConfig {
185+
name: "random-access/lance-tokio-local-disk".to_string(),
186+
storage: STORAGE_NVME.to_owned(),
187+
runtime: &runtime,
188+
indices: &indices,
189+
time_limit,
190+
target,
191+
},
175192
)
176193
}
177194
Format::Csv | Format::Arrow | Format::OnDiskDuckDB => unimplemented!(),

bench-vortex/src/measurements.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,23 @@ pub struct TimingMeasurement {
161161
pub name: String,
162162
pub target: Target,
163163
pub storage: String,
164-
pub time: Duration,
164+
pub runs: Vec<Duration>,
165+
}
166+
167+
impl TimingMeasurement {
168+
pub fn mean_time(&self) -> Duration {
169+
let len = self.runs.len();
170+
if len == 0 {
171+
vortex_panic!("cannot have no runs");
172+
}
173+
174+
let total_nanos: u128 = self.runs.iter().map(|d| d.as_nanos()).sum();
175+
let mean_nanos = total_nanos / len as u128;
176+
Duration::new(
177+
u64::try_from(mean_nanos / 1_000_000_000).vortex_unwrap(),
178+
u32::try_from(mean_nanos % 1_000_000_000).vortex_unwrap(),
179+
)
180+
}
165181
}
166182

167183
impl ToTable for TimingMeasurement {
@@ -171,7 +187,7 @@ impl ToTable for TimingMeasurement {
171187
name: self.name.clone(),
172188
target: self.target,
173189
unit: Cow::from("μs"),
174-
value: MeasurementValue::Int(self.time.as_micros()),
190+
value: MeasurementValue::Int(self.mean_time().as_micros()),
175191
}
176192
}
177193
}
@@ -182,7 +198,7 @@ impl ToJson for TimingMeasurement {
182198
name: self.name.clone(),
183199
storage: Some(self.storage.clone()),
184200
unit: Some(Cow::from("ns")),
185-
value: MeasurementValue::Int(self.time.as_nanos()),
201+
value: MeasurementValue::Int(self.mean_time().as_nanos()),
186202
bytes: None,
187203
time: None,
188204
commit_id: Cow::from(GIT_COMMIT_ID.as_str()),

0 commit comments

Comments
 (0)