use anyhow::Result; use criterion::{criterion_group, criterion_main, Criterion}; use once_cell::unsync::Lazy; use std::fs; use std::path::Path; use std::path::PathBuf; use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, VisitOperator, WasmFeatures};
/// A benchmark input. pubstruct 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>,
}
/// 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") => { iflet 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)
| wast::WastDirective::ModuleDefinition(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?;
}
}
FunctionSection(s) => { for item in s {
item?;
}
}
TableSection(s) => { for item in s {
item?;
}
}
MemorySection(s) => { for item in s {
item?;
}
}
TagSection(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?; iflet ElementKind::Active { offset_expr, .. } = item.kind { for op in offset_expr.get_operators_reader() {
op?;
}
} match item.items {
wasmparser::ElementItems::Functions(r) => { for op in r {
op?;
}
}
wasmparser::ElementItems::Expressions(_, r) => { for op in r {
op?;
}
}
}
}
}
DataSection(s) => { for item in s { let item = item?; iflet DataKind::Active { offset_expr, .. } = item.kind { for op in offset_expr.get_operators_reader() {
op?;
}
}
}
}
CodeSectionEntry(body) => { letmut reader = body.get_binary_reader(); for _ in0..reader.read_var_u32()? {
reader.read_var_u32()?;
reader.read::<wasmparser::ValType>()?;
} while !reader.eof() {
reader.visit_operator(&mut NopVisit)?;
}
}
// Component sections
ModuleSection { .. } => {}
InstanceSection(s) => { for item in s {
item?;
}
}
CoreTypeSection(s) => { for item in s {
item?;
}
}
ComponentSection { .. } => {}
ComponentInstanceSection(s) => { for item in s {
item?;
}
}
ComponentAliasSection(s) => { for item in s {
item?;
}
}
ComponentTypeSection(s) => { for item in s {
item?;
}
}
ComponentCanonicalSection(s) => { for item in s {
item?;
}
}
ComponentStartSection { .. } => {}
ComponentImportSection(s) => { for item in s {
item?;
}
}
ComponentExportSection(s) => { for item in s {
item?;
}
}
other => { // NB: if you hit this panic if you'd be so kind as to grep // through other locations in the code base that need to be // updated as well. As of the time of this writing the locations // might be: // // * src/bin/wasm-tools/objdump.rs // * src/bin/wasm-tools/dump.rs // * crates/wasm-encoder/src/reencode.rs // * crates/wasm-encoder/src/reencode/component.rs // * crates/wasmprinter/src/lib.rs // * crates/wit-component/src/gc.rs // // This is required due to the `#[non_exhaustive]` nature of // the `Payload` enum.
panic!("a new match statement should be added above for this case: {other:?}")
}
}
}
Ok(())
}
/// Returns the default benchmark inputs that are proper `wasmparser` benchmark /// test inputs. fn collect_benchmark_inputs() -> Vec<BenchmarkInput> { letmut ret = Vec::new();
collect_test_files("../../tests".as_ref(), &mut ret).unwrap(); // Sort to ideally get more deterministic perf that ignores filesystems
ret.sort_by_key(|p| p.path.clone());
ret
}
let test_inputs = once_cell::unsync::Lazy::new(collect_benchmark_inputs);
let parse_inputs = once_cell::unsync::Lazy::new(|| { letmut list = Vec::new(); for input in test_inputs.iter() { if read_all_wasm(&input.wasm).is_ok() {
list.push(&input.wasm);
}
}
list
});
c.bench_function("parse/tests", |b| {
Lazy::force(&parse_inputs);
b.iter(|| { for wasm in parse_inputs.iter() {
read_all_wasm(wasm).unwrap();
}
})
});
let validate_inputs = once_cell::unsync::Lazy::new(|| { letmut list = Vec::new(); for input in test_inputs.iter() { if skip_validation(&input.path) { continue;
}
log::debug!("Validating {}", input.path.display()); if validator().validate_all(&input.wasm).is_ok() {
list.push(&input.wasm);
}
}
list
});
c.bench_function("validate/tests", |b| {
Lazy::force(&validate_inputs);
b.iter(|| { for wasm in validate_inputs.iter() {
validator().validate_all(wasm).unwrap();
}
})
});
for file in std::fs::read_dir("benches").unwrap() { let file = file.unwrap(); let path = file.path(); if path.extension().and_then(|s| s.to_str()) != Some("wasm") { continue;
} let name = path.file_stem().unwrap().to_str().unwrap(); let wasm = Lazy::new(|| std::fs::read(&path).unwrap());
c.bench_function(&format!("validate/{name}"), |b| {
Lazy::force(&wasm);
b.iter(|| {
validator().validate_all(&wasm).unwrap();
})
});
c.bench_function(&format!("parse/{name}"), |b| {
Lazy::force(&wasm);
b.iter(|| {
read_all_wasm(&wasm).unwrap();
})
});
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.