@@ -2,9 +2,10 @@ use crate::suite::BenchmarkOrSuite;
22use anyhow:: { anyhow, Context , Result } ;
33use rand:: { rngs:: SmallRng , Rng , SeedableRng } ;
44use sightglass_data:: { Format , Measurement , Phase } ;
5+ use sightglass_recorder:: bench_api:: Engine ;
56use sightglass_recorder:: cpu_affinity:: bind_to_single_core;
67use sightglass_recorder:: measure:: Measurements ;
7- use sightglass_recorder:: { bench_api:: BenchApi , benchmark:: benchmark , measure:: MeasureType } ;
8+ use sightglass_recorder:: { bench_api:: BenchApi , benchmark, measure:: MeasureType } ;
89use std:: {
910 fs,
1011 io:: { self , BufWriter , Write } ,
@@ -105,9 +106,10 @@ pub struct BenchmarkCommand {
105106 #[ structopt( short( "d" ) , long( "working-dir" ) , parse( from_os_str) ) ]
106107 working_dir : Option < PathBuf > ,
107108
108- /// Stop measuring after the given phase (compilation/instantiation/execution).
109- #[ structopt( long( "stop-after" ) ) ]
110- stop_after_phase : Option < Phase > ,
109+ /// Benchmark only the given phase (compilation, instantiation, or
110+ /// execution). Benchmarks all phases if omitted.
111+ #[ structopt( long( "benchmark-phase" ) ) ]
112+ benchmark_phase : Option < Phase > ,
111113
112114 /// The significance level for confidence intervals. Typical values are 0.01
113115 /// and 0.05, which correspond to 99% and 95% confidence respectively. This
@@ -178,56 +180,98 @@ impl BenchmarkCommand {
178180 let bytes = fs:: read ( & wasm_file) . context ( "Attempting to read Wasm bytes" ) ?;
179181 log:: debug!( "Wasm benchmark size: {} bytes" , bytes. len( ) ) ;
180182
183+ let wasm_hash = {
184+ use std:: collections:: hash_map:: DefaultHasher ;
185+ use std:: hash:: { Hash , Hasher } ;
186+ let mut hasher = DefaultHasher :: new ( ) ;
187+ wasm_file. hash ( & mut hasher) ;
188+ hasher. finish ( )
189+ } ;
190+ let stdout = format ! ( "stdout-{:x}-{}.log" , wasm_hash, std:: process:: id( ) ) ;
191+ let stdout = Path :: new ( & stdout) ;
192+ let stderr = format ! ( "stderr-{:x}-{}.log" , wasm_hash, std:: process:: id( ) ) ;
193+ let stderr = Path :: new ( & stderr) ;
194+ let stdin = None ;
195+
181196 let mut measurements = Measurements :: new ( this_arch ( ) , engine, wasm_file) ;
182197 let mut measure = self . measure . build ( ) ;
183198
199+ // Create the bench API engine and cache it for reuse across all
200+ // iterations of this benchmark.
201+ let engine = Engine :: new (
202+ & mut bench_api,
203+ & working_dir,
204+ stdout,
205+ stderr,
206+ stdin,
207+ & mut measurements,
208+ & mut measure,
209+ self . engine_flags . as_deref ( ) ,
210+ ) ;
211+ let mut engine = Some ( engine) ;
212+
213+ // And if we are benchmarking just a post-compilation phase,
214+ // then eagerly compile the Wasm module for reuse.
215+ let mut module = None ;
216+ if let Some ( Phase :: Instantiation | Phase :: Execution ) = self . benchmark_phase {
217+ module = Some ( engine. take ( ) . unwrap ( ) . compile ( & bytes) ) ;
218+ }
219+
184220 // Run the benchmark (compilation, instantiation, and execution) several times in
185221 // this process.
186- for i in 0 ..self . iterations_per_process {
187- let wasm_hash = {
188- use std:: collections:: hash_map:: DefaultHasher ;
189- use std:: hash:: { Hash , Hasher } ;
190- let mut hasher = DefaultHasher :: new ( ) ;
191- wasm_file. hash ( & mut hasher) ;
192- hasher. finish ( )
193- } ;
194- let stdout = format ! ( "stdout-{:x}-{}-{}.log" , wasm_hash, std:: process:: id( ) , i) ;
195- let stdout = Path :: new ( & stdout) ;
196- let stderr = format ! ( "stderr-{:x}-{}-{}.log" , wasm_hash, std:: process:: id( ) , i) ;
197- let stderr = Path :: new ( & stderr) ;
198- let stdin = None ;
199-
200- benchmark (
201- & mut bench_api,
202- & working_dir,
203- stdout,
204- stderr,
205- stdin,
206- & bytes,
207- self . stop_after_phase . clone ( ) ,
208- self . engine_flags . as_deref ( ) ,
209- & mut measure,
210- & mut measurements,
211- ) ?;
222+ for _ in 0 ..self . iterations_per_process {
223+ match self . benchmark_phase {
224+ None => {
225+ let new_engine = benchmark:: all ( engine. take ( ) . unwrap ( ) , & bytes) ?;
226+ engine = Some ( new_engine) ;
227+ }
228+ Some ( Phase :: Compilation ) => {
229+ let new_engine =
230+ benchmark:: compilation ( engine. take ( ) . unwrap ( ) , & bytes) ?;
231+ engine = Some ( new_engine) ;
232+ }
233+ Some ( Phase :: Instantiation ) => {
234+ let new_module = benchmark:: instantiation ( module. take ( ) . unwrap ( ) ) ?;
235+ module = Some ( new_module) ;
236+ }
237+ Some ( Phase :: Execution ) => {
238+ let new_module = benchmark:: execution ( module. take ( ) . unwrap ( ) ) ?;
239+ module = Some ( new_module) ;
240+ }
241+ }
212242
213243 self . check_output ( Path :: new ( wasm_file) , stdout, stderr) ?;
214- measurements. next_iteration ( ) ;
244+ engine
245+ . as_mut ( )
246+ . map ( |e| e. measurements ( ) )
247+ . or_else ( || module. as_mut ( ) . map ( |m| m. measurements ( ) ) )
248+ . unwrap ( )
249+ . next_iteration ( ) ;
215250 }
216251
252+ drop ( ( engine, module) ) ;
217253 all_measurements. extend ( measurements. finish ( ) ) ;
218254 }
219255 }
220256
257+ // If we are only benchmarking one phase then filter out any
258+ // measurements for other phases. These get included because we have to
259+ // compile at least once to measure instantiation, for example.
260+ if let Some ( phase) = self . benchmark_phase {
261+ all_measurements. retain ( |m| m. phase == phase) ;
262+ }
263+
221264 self . write_results ( & all_measurements, & mut output_file) ?;
222265 Ok ( ( ) )
223266 }
224267
225268 /// Assert that our actual `stdout` and `stderr` match our expectations.
226269 fn check_output ( & self , wasm_file : & Path , stdout : & Path , stderr : & Path ) -> Result < ( ) > {
227- // If we aren't going through all phases and executing the Wasm, then we
228- // won't have any actual output to check.
229- if self . stop_after_phase . is_some ( ) {
230- return Ok ( ( ) ) ;
270+ match self . benchmark_phase {
271+ None | Some ( Phase :: Execution ) => { }
272+ // If we aren't executing the Wasm, then we won't have any actual
273+ // output to check.
274+ Some ( Phase :: Compilation | Phase :: Instantiation ) => return Ok ( ( ) ) ,
231275 }
232276
233277 let wasm_file_dir: PathBuf = if let Some ( dir) = wasm_file. parent ( ) {
@@ -326,8 +370,8 @@ impl BenchmarkCommand {
326370 command. env ( "WASM_BENCH_USE_SMALL_WORKLOAD" , "1" ) ;
327371 }
328372
329- if let Some ( phase) = self . stop_after_phase {
330- command. arg ( "--stop-after " ) . arg ( phase. to_string ( ) ) ;
373+ if let Some ( phase) = self . benchmark_phase {
374+ command. arg ( "--benchmark-phase " ) . arg ( phase. to_string ( ) ) ;
331375 }
332376
333377 if let Some ( flags) = & self . engine_flags {
0 commit comments