11#[ macro_use]
22extern crate criterion;
33
4- use criterion:: Criterion ;
5- use std:: fs:: { read_dir , File } ;
6- use std:: io:: Read ;
4+ use criterion:: { black_box , Criterion } ;
5+ use std:: fs;
6+ use std:: io;
77use std:: path:: Path ;
88use std:: path:: PathBuf ;
99use wasmparser:: {
@@ -22,65 +22,208 @@ const VALIDATOR_CONFIG: Option<ValidatingParserConfig> = Some(ValidatingParserCo
2222 } ,
2323} ) ;
2424
25- fn read_all_wasm < ' a , T > ( mut d : T )
25+ /// A benchmark input.
26+ pub struct BenchmarkInput {
27+ /// The path to the benchmark file important for handling errors.
28+ pub path : PathBuf ,
29+ /// The encoded Wasm module that is run by the benchmark.
30+ pub wasm : Vec < u8 > ,
31+ }
32+
33+ impl BenchmarkInput {
34+ /// Creates a new benchmark input.
35+ pub fn new ( test_path : PathBuf , encoded_wasm : Vec < u8 > ) -> Self {
36+ Self {
37+ path : test_path,
38+ wasm : encoded_wasm,
39+ }
40+ }
41+ }
42+
43+ /// Read a `.wat` formatted benchmark test file as benchmark input.
44+ fn read_wat_module ( path : & PathBuf ) -> BenchmarkInput {
45+ let encoded_wasm =
46+ wat:: parse_file ( path) . expect ( "encountered error while parsing `.wat` file into `.wasm`" ) ;
47+ BenchmarkInput :: new ( path. clone ( ) , encoded_wasm)
48+ }
49+
50+ /// Read a `.wast` formatted benchmark test file as benchmark input.
51+ ///
52+ /// We simply pull out all the module directives of the `.wast` file and return them.
53+ fn read_wast_module ( path : & PathBuf ) -> Vec < BenchmarkInput > {
54+ let mut wast_file = fs:: File :: open ( path)
55+ . ok ( )
56+ . expect ( "encountered error while reading `.wast` benchmark file" ) ;
57+ let mut wast_file_contents = String :: new ( ) ;
58+ use io:: Read as _;
59+ wast_file
60+ . read_to_string ( & mut wast_file_contents)
61+ . expect ( "encountered error while reading `.wast` benchmark file to string" ) ;
62+ let mut inputs = Vec :: new ( ) ;
63+ if let Ok ( parse_buffer) = wast:: parser:: ParseBuffer :: new ( & wast_file_contents) {
64+ ' outer: while let Ok ( directive) = wast:: parser:: parse :: < wast:: WastDirective > ( & parse_buffer)
65+ {
66+ match directive {
67+ wast:: WastDirective :: Module ( mut module) => {
68+ let encoded_wasm = module
69+ . encode ( )
70+ . expect ( "encountered error while encoding the Wast module into Wasm" ) ;
71+ inputs. push ( BenchmarkInput :: new ( path. clone ( ) , encoded_wasm) ) ;
72+ }
73+ _ => continue ' outer,
74+ }
75+ }
76+ }
77+ inputs
78+ }
79+
80+ /// Visits all directory entries within the given directory path.
81+ ///
82+ /// - `pred` can be used to filter some directories, e.g. all directories named
83+ /// `"proposals"`.
84+ /// - `cb` is the callback that is being called for every file within the non
85+ /// filtered and visited directories.
86+ fn visit_dirs < P , F > ( dir : & Path , pred : & P , cb : & mut F ) -> io:: Result < ( ) >
2687where
27- T : WasmDecoder < ' a > ,
88+ P : Fn ( & fs:: DirEntry ) -> bool ,
89+ F : FnMut ( & fs:: DirEntry ) ,
2890{
29- loop {
30- match * d. read ( ) {
31- ParserState :: Error ( ref e) => panic ! ( "unexpected error while reading Wasm: {}" , e) ,
32- ParserState :: EndWasm => return ,
33- _ => ( ) ,
91+ if dir. is_dir ( ) {
92+ for entry in fs:: read_dir ( dir) ? {
93+ let entry = entry?;
94+ let path = entry. path ( ) ;
95+ if path. is_dir ( ) && pred ( & entry) {
96+ visit_dirs ( & path, pred, cb) ?;
97+ } else {
98+ cb ( & entry) ;
99+ }
34100 }
35101 }
102+ Ok ( ( ) )
36103}
37104
38- fn read_file_data ( path : & PathBuf ) -> Vec < u8 > {
39- let mut data = Vec :: new ( ) ;
40- let mut f = File :: open ( path) . ok ( ) . unwrap ( ) ;
41- f. read_to_end ( & mut data) . unwrap ( ) ;
42- data
105+ /// Returns a vector of all found benchmark input files under the given directory.
106+ ///
107+ /// Benchmark input files can be `.wat` or `.wast` formatted files.
108+ /// For `.wast` files we pull out all the module directives and run them in the benchmarks.
109+ fn collect_test_files < P > ( path : P ) -> Vec < BenchmarkInput >
110+ where
111+ P : AsRef < Path > ,
112+ {
113+ let mut file_contents: Vec < BenchmarkInput > = vec ! [ ] ;
114+ visit_dirs (
115+ path. as_ref ( ) ,
116+ & |_| true , // accept all benchmarks
117+ & mut |dir_entry| {
118+ let ext: Option < String > = dir_entry
119+ . path ( )
120+ . extension ( )
121+ . and_then ( |ext| ext. to_str ( ) . map ( |str| str. to_string ( ) ) ) ;
122+ match ext. as_ref ( ) . map ( |string| string. as_str ( ) ) {
123+ Some ( "wat" ) => file_contents. push ( read_wat_module ( & dir_entry. path ( ) ) ) ,
124+ Some ( "wast" ) => {
125+ for wasm_module in read_wast_module ( & dir_entry. path ( ) ) {
126+ file_contents. push ( wasm_module)
127+ }
128+ }
129+ _ => ( ) ,
130+ }
131+ } ,
132+ )
133+ . expect ( "encountered error while reading test directory" ) ;
134+ file_contents
43135}
44136
45- fn collect_test_files < P > ( path : P ) -> Vec < Vec < u8 > >
137+ /// Reads the input given the Wasm parser or validator.
138+ ///
139+ /// The `path` specifies which benchmark input file we are currently operating on
140+ /// so that we can report better errors in case of failures.
141+ fn read_all_wasm < ' a , T > ( path : & PathBuf , mut d : T )
46142where
47- P : AsRef < Path > ,
143+ T : WasmDecoder < ' a > ,
48144{
49- let mut file_contents: Vec < Vec < u8 > > = vec ! [ ] ;
50- for entry in read_dir ( path) . expect ( "cannot find the benchmark test files" ) {
51- let dir = entry. unwrap ( ) ;
52- if !dir. file_type ( ) . unwrap ( ) . is_file ( ) {
53- continue ;
145+ loop {
146+ match * d. read ( ) {
147+ ParserState :: Error ( ref e) => {
148+ panic ! ( "unexpected error while reading Wasm from {:?}: {}" , path, e)
149+ }
150+ ParserState :: EndWasm => return ,
151+ _ => ( ) ,
54152 }
55- file_contents. push ( read_file_data ( & dir. path ( ) ) ) ;
56153 }
57- file_contents
154+ }
155+
156+ /// Returns the default benchmark inputs that are proper `wasmparser` benchmark
157+ /// test inputs.
158+ fn collect_benchmark_inputs ( ) -> Vec < BenchmarkInput > {
159+ let from_testsuite = collect_test_files ( "../../testsuite" ) ;
160+ let from_tests = collect_test_files ( "../../tests" ) ;
161+ from_testsuite
162+ . into_iter ( )
163+ . chain ( from_tests. into_iter ( ) )
164+ . collect :: < Vec < _ > > ( )
58165}
59166
60167fn it_works_benchmark ( c : & mut Criterion ) {
61- let mut data = collect_test_files ( "../../tests" ) ;
62- c. bench_function ( "it works benchmark" , move |b| {
63- for d in & mut data {
64- b. iter ( || read_all_wasm ( Parser :: new ( d. as_slice ( ) ) ) ) ;
168+ let mut inputs = collect_benchmark_inputs ( ) ;
169+ // Filter out all benchmark inputs that fail to parse via `wasmparser`.
170+ inputs. retain ( |input| {
171+ let mut parser = Parser :: new ( input. wasm . as_slice ( ) ) ;
172+ ' outer: loop {
173+ match parser. read ( ) {
174+ ParserState :: Error ( _) => break ' outer false ,
175+ ParserState :: EndWasm => break ' outer true ,
176+ _ => continue ,
177+ }
65178 }
66179 } ) ;
180+ c. bench_function ( "it works benchmark" , move |b| {
181+ b. iter ( || {
182+ for input in & mut inputs {
183+ let _ = black_box ( read_all_wasm (
184+ & input. path ,
185+ Parser :: new ( input. wasm . as_slice ( ) ) ,
186+ ) ) ;
187+ }
188+ } )
189+ } ) ;
67190}
68191
69192fn validator_not_fails_benchmark ( c : & mut Criterion ) {
70- let mut data = collect_test_files ( "../../tests" ) ;
71- c. bench_function ( "validator no fails benchmark" , move |b| {
72- for d in & mut data {
73- b. iter ( || read_all_wasm ( ValidatingParser :: new ( d. as_slice ( ) , VALIDATOR_CONFIG ) ) ) ;
193+ let mut inputs = collect_benchmark_inputs ( ) ;
194+ // Filter out all benchmark inputs that fail to validate via `wasmparser`.
195+ inputs. retain ( |input| {
196+ let mut parser = ValidatingParser :: new ( input. wasm . as_slice ( ) , VALIDATOR_CONFIG ) ;
197+ ' outer: loop {
198+ match parser. read ( ) {
199+ ParserState :: Error ( _) => break ' outer false ,
200+ ParserState :: EndWasm => break ' outer true ,
201+ _ => continue ,
202+ }
74203 }
75204 } ) ;
205+ c. bench_function ( "validator no fails benchmark" , move |b| {
206+ b. iter ( || {
207+ for input in & mut inputs {
208+ let _ = black_box ( read_all_wasm (
209+ & input. path ,
210+ ValidatingParser :: new ( input. wasm . as_slice ( ) , VALIDATOR_CONFIG ) ,
211+ ) ) ;
212+ }
213+ } ) ;
214+ } ) ;
76215}
77216
78217fn validate_benchmark ( c : & mut Criterion ) {
79- let mut data = collect_test_files ( "../../tests" ) ;
218+ let mut inputs = collect_benchmark_inputs ( ) ;
219+ // Filter out all benchmark inputs that fail to validate via `wasmparser`.
220+ inputs. retain ( |input| validate ( input. wasm . as_slice ( ) , VALIDATOR_CONFIG ) . is_ok ( ) ) ;
80221 c. bench_function ( "validate benchmark" , move |b| {
81- for d in & mut data {
82- b. iter ( || validate ( & d, VALIDATOR_CONFIG ) ) ;
83- }
222+ b. iter ( || {
223+ for input in & mut inputs {
224+ let _ = black_box ( validate ( input. wasm . as_slice ( ) , VALIDATOR_CONFIG ) ) ;
225+ }
226+ } )
84227 } ) ;
85228}
86229
0 commit comments