diff options
Diffstat (limited to 'third_party/rust/wasmparser/benches/benchmark.rs')
-rw-r--r-- | third_party/rust/wasmparser/benches/benchmark.rs | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/benches/benchmark.rs b/third_party/rust/wasmparser/benches/benchmark.rs new file mode 100644 index 0000000000..25a7257cb0 --- /dev/null +++ b/third_party/rust/wasmparser/benches/benchmark.rs @@ -0,0 +1,236 @@ +#[macro_use] +extern crate criterion; + +use anyhow::Result; +use criterion::Criterion; +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, WasmFeatures}; + +/// A benchmark input. +pub struct BenchmarkInput { + /// The path to the benchmark file important for handling errors. + pub path: PathBuf, + /// The encoded Wasm module that is run by the benchmark. + pub wasm: Vec<u8>, +} + +impl BenchmarkInput { + /// Creates a new benchmark input. + pub fn new(test_path: PathBuf, encoded_wasm: Vec<u8>) -> Self { + Self { + path: test_path, + wasm: encoded_wasm, + } + } +} + +/// Returns a vector of all found benchmark input files under the given directory. +/// +/// Benchmark input files can be `.wat` or `.wast` formatted files. +/// For `.wast` files we pull out all the module directives and run them in the benchmarks. +fn collect_test_files(path: &Path, list: &mut Vec<BenchmarkInput>) -> Result<()> { + for entry in path.read_dir()? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + collect_test_files(&path, list)?; + continue; + } + match path.extension().and_then(|ext| ext.to_str()) { + Some("wasm") => { + let wasm = fs::read(&path)?; + list.push(BenchmarkInput::new(path, wasm)); + } + Some("wat") | Some("txt") => { + if let Ok(wasm) = wat::parse_file(&path) { + list.push(BenchmarkInput::new(path, wasm)); + } + } + Some("wast") => { + let contents = fs::read_to_string(&path)?; + let buf = match wast::parser::ParseBuffer::new(&contents) { + Ok(buf) => buf, + Err(_) => continue, + }; + let wast: wast::Wast<'_> = match wast::parser::parse(&buf) { + Ok(wast) => wast, + Err(_) => continue, + }; + for directive in wast.directives { + match directive { + wast::WastDirective::Module(mut module) => { + let wasm = module.encode()?; + list.push(BenchmarkInput::new(path.clone(), wasm)); + } + _ => continue, + } + } + } + _ => (), + } + } + Ok(()) +} + +/// Reads the input given the Wasm parser or validator. +/// +/// The `path` specifies which benchmark input file we are currently operating on +/// so that we can report better errors in case of failures. +fn read_all_wasm(wasm: &[u8]) -> Result<()> { + use Payload::*; + for item in Parser::new(0).parse_all(wasm) { + match item? { + TypeSection(s) => { + for item in s { + item?; + } + } + ImportSection(s) => { + for item in s { + item?; + } + } + AliasSection(s) => { + for item in s { + item?; + } + } + InstanceSection(s) => { + for item in s { + for arg in item?.args()? { + arg?; + } + } + } + ModuleSection(s) => { + for item in s { + item?; + } + } + FunctionSection(s) => { + for item in s { + item?; + } + } + TableSection(s) => { + for item in s { + item?; + } + } + MemorySection(s) => { + for item in s { + item?; + } + } + GlobalSection(s) => { + for item in s { + for op in item?.init_expr.get_operators_reader() { + op?; + } + } + } + ExportSection(s) => { + for item in s { + item?; + } + } + ElementSection(s) => { + for item in s { + let item = item?; + if let ElementKind::Active { init_expr, .. } = item.kind { + for op in init_expr.get_operators_reader() { + op?; + } + } + for op in item.items.get_items_reader()? { + op?; + } + } + } + DataSection(s) => { + for item in s { + let item = item?; + if let DataKind::Active { init_expr, .. } = item.kind { + for op in init_expr.get_operators_reader() { + op?; + } + } + } + } + CodeSectionEntry(body) => { + for local in body.get_locals_reader()? { + local?; + } + for op in body.get_operators_reader()? { + op?; + } + } + + Version { .. } + | StartSection { .. } + | DataCountSection { .. } + | UnknownSection { .. } + | CustomSection { .. } + | CodeSectionStart { .. } + | ModuleCodeSectionStart { .. } + | ModuleCodeSectionEntry { .. } + | End => {} + } + } + Ok(()) +} + +/// Returns the default benchmark inputs that are proper `wasmparser` benchmark +/// test inputs. +fn collect_benchmark_inputs() -> Vec<BenchmarkInput> { + let mut ret = Vec::new(); + collect_test_files("../../tests".as_ref(), &mut ret).unwrap(); + return ret; +} + +fn it_works_benchmark(c: &mut Criterion) { + let mut inputs = collect_benchmark_inputs(); + // Filter out all benchmark inputs that fail to parse via `wasmparser`. + inputs.retain(|input| read_all_wasm(input.wasm.as_slice()).is_ok()); + c.bench_function("it works benchmark", move |b| { + b.iter(|| { + for input in &mut inputs { + read_all_wasm(input.wasm.as_slice()).unwrap(); + } + }) + }); +} + +fn validate_benchmark(c: &mut Criterion) { + fn validator() -> Validator { + let mut ret = Validator::new(); + ret.wasm_features(WasmFeatures { + reference_types: true, + multi_value: true, + simd: true, + module_linking: true, + bulk_memory: true, + threads: true, + tail_call: true, + multi_memory: true, + memory64: true, + deterministic_only: false, + }); + return ret; + } + let mut inputs = collect_benchmark_inputs(); + // Filter out all benchmark inputs that fail to validate via `wasmparser`. + inputs.retain(|input| validator().validate_all(&input.wasm).is_ok()); + c.bench_function("validate benchmark", move |b| { + b.iter(|| { + for input in &mut inputs { + validator().validate_all(&input.wasm).unwrap(); + } + }) + }); +} + +criterion_group!(benchmark, it_works_benchmark, validate_benchmark); +criterion_main!(benchmark); |