Skip to content

Commit 754adcc

Browse files
committed
perf: measure warmed collection rebind VMs
1 parent 68e39a7 commit 754adcc

1 file changed

Lines changed: 96 additions & 23 deletions

File tree

examples/collection_rebind_bench.rs

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::env;
22
use std::hint::black_box;
33
use std::time::{Duration, Instant};
44

5-
use vm::{JitConfig, Program, Value, Vm, VmStatus, compile_source};
5+
use vm::{JitConfig, JitTraceTerminal, Program, Value, Vm, VmStatus, compile_source};
66

77
const DEFAULT_WIDTH: usize = 256;
88
const DEFAULT_ITERATIONS: usize = 50_000;
@@ -38,12 +38,25 @@ impl ExecMode {
3838
}
3939
}
4040

41+
struct Measurement {
42+
samples: Vec<Duration>,
43+
native_traces: usize,
44+
call_boundary_traces: usize,
45+
loop_back_traces: usize,
46+
native_execs: u64,
47+
trace_exits: u64,
48+
native_loop_backs: u64,
49+
helper_fallbacks: u64,
50+
generic_builtin_calls: u64,
51+
}
52+
4153
#[derive(Clone, Copy, Debug)]
4254
struct Config {
4355
width: usize,
4456
iterations: usize,
4557
samples: usize,
4658
jit: bool,
59+
jit_only: bool,
4760
}
4861

4962
impl Config {
@@ -53,6 +66,7 @@ impl Config {
5366
iterations: DEFAULT_ITERATIONS,
5467
samples: DEFAULT_SAMPLES,
5568
jit: false,
69+
jit_only: false,
5670
};
5771
let mut args = env::args().skip(1);
5872
while let Some(arg) = args.next() {
@@ -61,6 +75,10 @@ impl Config {
6175
"--iterations" => config.iterations = parse_usize("--iterations", args.next())?,
6276
"--samples" => config.samples = parse_usize("--samples", args.next())?,
6377
"--jit" => config.jit = true,
78+
"--jit-only" => {
79+
config.jit = true;
80+
config.jit_only = true;
81+
}
6482
other => return Err(format!("unknown argument '{other}'")),
6583
}
6684
}
@@ -87,7 +105,9 @@ fn main() {
87105

88106
fn run() -> Result<(), String> {
89107
let config = Config::parse()?;
90-
let modes: &[ExecMode] = if config.jit {
108+
let modes: &[ExecMode] = if config.jit_only {
109+
&[ExecMode::Jit]
110+
} else if config.jit {
91111
&[ExecMode::Interpreter, ExecMode::Jit]
92112
} else {
93113
&[ExecMode::Interpreter]
@@ -96,23 +116,32 @@ fn run() -> Result<(), String> {
96116
for workload in [Workload::Array, Workload::Map] {
97117
let program = compile_workload(workload, config.width, config.iterations)?;
98118
for mode in modes {
99-
let samples = measure(&program, workload, *mode, config)?;
100-
let mut sorted = samples.clone();
119+
let measurement = measure(&program, workload, *mode, config)?;
120+
let mut sorted = measurement.samples.clone();
101121
sorted.sort_unstable();
102122
let median = sorted[sorted.len() / 2];
103-
let sample_ns = samples
123+
let sample_ns = measurement
124+
.samples
104125
.iter()
105126
.map(Duration::as_nanos)
106127
.map(|value| value.to_string())
107128
.collect::<Vec<_>>()
108129
.join(",");
109130
println!(
110-
"workload={} mode={} width={} iterations={} median_ns={} samples_ns={}",
131+
"workload={} mode={} width={} iterations={} warmup_runs=1 reused_vm=true median_ns={} native_traces={} call_boundary_traces={} loop_back_traces={} native_execs={} trace_exits={} native_loop_backs={} helper_fallbacks={} generic_builtin_calls={} samples_ns={}",
111132
workload.label(),
112133
mode.label(),
113134
config.width,
114135
config.iterations,
115136
median.as_nanos(),
137+
measurement.native_traces,
138+
measurement.call_boundary_traces,
139+
measurement.loop_back_traces,
140+
measurement.native_execs,
141+
measurement.trace_exits,
142+
measurement.native_loop_backs,
143+
measurement.helper_fallbacks,
144+
measurement.generic_builtin_calls,
116145
sample_ns
117146
);
118147
}
@@ -157,34 +186,78 @@ fn measure(
157186
workload: Workload,
158187
mode: ExecMode,
159188
config: Config,
160-
) -> Result<Vec<Duration>, String> {
161-
run_once(program, workload, mode, config)?;
189+
) -> Result<Measurement, String> {
190+
let mut vm = configured_vm(program, mode);
191+
let warmup = vm
192+
.run()
193+
.map_err(|err| format!("{} {} warmup failed: {err}", workload.label(), mode.label()))?;
194+
verify_result(&vm, warmup, config)?;
195+
if matches!(mode, ExecMode::Jit) && vm.jit_native_exec_count() == 0 {
196+
return Err(format!(
197+
"{} warmup did not execute native traces:\n{}",
198+
workload.label(),
199+
vm.dump_jit_info()
200+
));
201+
}
202+
203+
let snapshot_before = vm.jit_snapshot();
204+
let metrics_before = snapshot_before.metrics;
205+
let native_traces = snapshot_before.traces.len();
206+
let call_boundary_traces = snapshot_before
207+
.traces
208+
.iter()
209+
.filter(|trace| trace.has_call)
210+
.count();
211+
let loop_back_traces = snapshot_before
212+
.traces
213+
.iter()
214+
.filter(|trace| trace.terminal == JitTraceTerminal::LoopBack)
215+
.count();
162216
let mut samples = Vec::with_capacity(config.samples);
217+
let mut generic_builtin_calls = 0u64;
163218
for _ in 0..config.samples {
164-
let mut vm = configured_vm(program, mode);
219+
vm.reset_for_reuse();
165220
let started = Instant::now();
166221
let status = vm
167222
.run()
168223
.map_err(|err| format!("{} {} run failed: {err}", workload.label(), mode.label()))?;
169224
let elapsed = started.elapsed();
170225
verify_result(&vm, status, config)?;
226+
generic_builtin_calls = generic_builtin_calls
227+
.saturating_add(vm.interpreter_metrics_snapshot().generic_builtin_call_count);
171228
black_box(vm.stack());
172229
samples.push(elapsed);
173230
}
174-
Ok(samples)
175-
}
176-
177-
fn run_once(
178-
program: &Program,
179-
workload: Workload,
180-
mode: ExecMode,
181-
config: Config,
182-
) -> Result<(), String> {
183-
let mut vm = configured_vm(program, mode);
184-
let status = vm
185-
.run()
186-
.map_err(|err| format!("{} {} warmup failed: {err}", workload.label(), mode.label()))?;
187-
verify_result(&vm, status, config)
231+
let snapshot_after = vm.jit_snapshot();
232+
if snapshot_after.traces.len() != native_traces {
233+
return Err(format!(
234+
"{} {} compiled additional traces during measured runs: warmup={} measured={}",
235+
workload.label(),
236+
mode.label(),
237+
native_traces,
238+
snapshot_after.traces.len()
239+
));
240+
}
241+
let metrics_after = snapshot_after.metrics;
242+
Ok(Measurement {
243+
samples,
244+
native_traces,
245+
call_boundary_traces,
246+
loop_back_traces,
247+
native_execs: metrics_after
248+
.native_trace_exec_count
249+
.saturating_sub(metrics_before.native_trace_exec_count),
250+
trace_exits: metrics_after
251+
.trace_exit_count
252+
.saturating_sub(metrics_before.trace_exit_count),
253+
native_loop_backs: metrics_after
254+
.native_loop_back_count
255+
.saturating_sub(metrics_before.native_loop_back_count),
256+
helper_fallbacks: metrics_after
257+
.helper_fallback_count
258+
.saturating_sub(metrics_before.helper_fallback_count),
259+
generic_builtin_calls,
260+
})
188261
}
189262

190263
fn configured_vm(program: &Program, mode: ExecMode) -> Vm {

0 commit comments

Comments
 (0)