@@ -2,9 +2,10 @@ use crate::suite::BenchmarkOrSuite;
2
2
use anyhow:: { anyhow, Context , Result } ;
3
3
use rand:: { rngs:: SmallRng , Rng , SeedableRng } ;
4
4
use sightglass_data:: { Format , Measurement , Phase } ;
5
+ use sightglass_recorder:: bench_api:: Engine ;
5
6
use sightglass_recorder:: cpu_affinity:: bind_to_single_core;
6
7
use 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 } ;
8
9
use std:: {
9
10
fs,
10
11
io:: { self , BufWriter , Write } ,
@@ -105,9 +106,10 @@ pub struct BenchmarkCommand {
105
106
#[ structopt( short( "d" ) , long( "working-dir" ) , parse( from_os_str) ) ]
106
107
working_dir : Option < PathBuf > ,
107
108
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 > ,
111
113
112
114
/// The significance level for confidence intervals. Typical values are 0.01
113
115
/// and 0.05, which correspond to 99% and 95% confidence respectively. This
@@ -178,56 +180,98 @@ impl BenchmarkCommand {
178
180
let bytes = fs:: read ( & wasm_file) . context ( "Attempting to read Wasm bytes" ) ?;
179
181
log:: debug!( "Wasm benchmark size: {} bytes" , bytes. len( ) ) ;
180
182
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
+
181
196
let mut measurements = Measurements :: new ( this_arch ( ) , engine, wasm_file) ;
182
197
let mut measure = self . measure . build ( ) ;
183
198
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
+
184
220
// Run the benchmark (compilation, instantiation, and execution) several times in
185
221
// 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
+ }
212
242
213
243
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 ( ) ;
215
250
}
216
251
252
+ drop ( ( engine, module) ) ;
217
253
all_measurements. extend ( measurements. finish ( ) ) ;
218
254
}
219
255
}
220
256
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
+
221
264
self . write_results ( & all_measurements, & mut output_file) ?;
222
265
Ok ( ( ) )
223
266
}
224
267
225
268
/// Assert that our actual `stdout` and `stderr` match our expectations.
226
269
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 ( ( ) ) ,
231
275
}
232
276
233
277
let wasm_file_dir: PathBuf = if let Some ( dir) = wasm_file. parent ( ) {
@@ -326,8 +370,8 @@ impl BenchmarkCommand {
326
370
command. env ( "WASM_BENCH_USE_SMALL_WORKLOAD" , "1" ) ;
327
371
}
328
372
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 ( ) ) ;
331
375
}
332
376
333
377
if let Some ( flags) = & self . engine_flags {
0 commit comments