@@ -2,7 +2,7 @@ use std::env;
22use std:: hint:: black_box;
33use 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
77const DEFAULT_WIDTH : usize = 256 ;
88const 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 ) ]
4254struct Config {
4355 width : usize ,
4456 iterations : usize ,
4557 samples : usize ,
4658 jit : bool ,
59+ jit_only : bool ,
4760}
4861
4962impl 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
88106fn 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
190263fn configured_vm ( program : & Program , mode : ExecMode ) -> Vm {
0 commit comments