diff --git a/benches/interpreter.rs b/benches/interpreter.rs new file mode 100644 index 0000000..e1711f5 --- /dev/null +++ b/benches/interpreter.rs @@ -0,0 +1,54 @@ +#![feature(test)] + +extern crate test; +extern crate parity_wasm; + +use test::Bencher; +use parity_wasm::builder::module; +use parity_wasm::elements::{ExportEntry, Internal, ImportEntry, External, Opcodes, Opcode}; +use parity_wasm::interpreter::{ProgramInstance, ModuleInstanceInterface, RuntimeValue}; + +#[bench] +fn export_entry_performance(b: &mut Bencher) { + // create module with 1000 functions + const NUM_FUNCTIONS: u32 = 1000; + let mut callee_module = module(); + for i in 0..NUM_FUNCTIONS { + callee_module = callee_module + .with_export(ExportEntry::new(format!("func{}", i), Internal::Function(i))) + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I32Const(i as i32), + Opcode::End, + ])).build() + .build(); + } + let callee_module = callee_module.build(); + + // create module which call one of 1000 functions + let caller_module = module() + .with_import(ImportEntry::new("callee_module".into(), "func500".into(), External::Function(0))) + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::Call(0), + Opcode::I32Const(1000), + Opcode::I32Add, + Opcode::End, + ])).build() + .build() + .build(); + + // add both modules to program + let program = ProgramInstance::new().unwrap(); + program.add_module("callee_module", callee_module, None).unwrap(); + let caller_module = program.add_module("caller_module", caller_module, None).unwrap(); + + // run bench + b.iter(|| + assert_eq!(caller_module.execute_index(1, vec![].into()).unwrap(), Some(RuntimeValue::I32(1500))) + ); + + // test export_entry_performance ... bench: 3,497 ns/iter (+/- 200) +} diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index a2df35b..1f7912b 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -8,7 +8,6 @@ use interpreter::program::ProgramInstanceEssence; use interpreter::table::TableInstance; use interpreter::variable::{VariableInstance, VariableType}; -// TODO: cache Internal-s to fasten access /// Module imports. pub struct ModuleImports { /// Program instance. diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3e0892f..1d59c06 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -74,7 +74,7 @@ mod variable; mod tests; pub use self::memory::MemoryInstance; -pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams}; +pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature}; pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 29b5613..12e34fa 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -98,6 +98,8 @@ pub struct ModuleInstance { functions_labels: HashMap>, /// Module imports. imports: ModuleImports, + /// Module exports. + exports: HashMap>, /// Tables. tables: Vec>, /// Linear memory regions. @@ -211,6 +213,7 @@ impl ModuleInstance { name: name, module: module, imports: imports, + exports: HashMap::new(), functions_labels: HashMap::new(), memory: memory, tables: tables, @@ -236,19 +239,27 @@ impl ModuleInstance { if let Some(export_section) = self.module.export_section() { for export in export_section.entries() { match export.internal() { - &Internal::Function(function_index) => - self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?, - &Internal::Global(global_index) => + &Internal::Function(function_index) => { + self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); + }, + &Internal::Global(global_index) => { self.global(ItemIndex::IndexSpace(global_index), None) .and_then(|g| if g.is_mutable() { Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) } else { Ok(()) - })?, - &Internal::Memory(memory_index) => - self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?, - &Internal::Table(table_index) => - self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?, + })?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index)); + }, + &Internal::Memory(memory_index) => { + self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index)); + }, + &Internal::Table(table_index) => { + self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index)); + }, } } } @@ -422,15 +433,15 @@ impl ModuleInstanceInterface for ModuleInstance { } fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { - let index = self.module.export_section() - .ok_or(Error::Function("missing export section".into())) - .and_then(|s| s.entries().iter() - .find(|e| e.field() == name && match e.internal() { - &Internal::Function(_) => true, + let index = self.exports.get(name) + .ok_or(Error::Function(format!("missing exports with name {}", name))) + .and_then(|l| l.iter() + .find(|i| match i { + &&Internal::Function(_) => true, _ => false, }) - .ok_or(Error::Function(format!("missing export section exported function with name {}", name))) - .map(|e| match e.internal() { + .ok_or(Error::Function(format!("missing exported function with name {}", name))) + .map(|i| match i { &Internal::Function(index) => index, _ => unreachable!(), // checked couple of lines above }) @@ -439,24 +450,24 @@ impl ModuleInstanceInterface for ModuleInstance { } fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result { - self.module.export_section() - .ok_or(Error::Program(format!("trying to import {} from module without export section", name))) - .and_then(|s| s.entries().iter() - .find(|e| e.field() == name && match required_type { + self.exports.get(name) + .ok_or(Error::Function(format!("missing exports with name {}", name))) + .and_then(|l| l.iter() + .find(|i| match required_type { &ExportEntryType::Any => true, - &ExportEntryType::Global(global_type) => match e.internal() { - &Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), + &ExportEntryType::Global(global_type) => match i { + &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), _ => false, }, - &ExportEntryType::Function(ref required_type) => match e.internal() { - &Internal::Function(function_index) => + &ExportEntryType::Function(ref required_type) => match i { + &&Internal::Function(function_index) => self.function_type(ItemIndex::IndexSpace(function_index)) .map(|ft| ft == *required_type) .unwrap_or(false), _ => false, }, }) - .map(|e| *e.internal()) + .map(|i| *i) .ok_or(Error::Program(format!("unresolved import {}", name)))) } @@ -653,6 +664,7 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, ex } impl<'a> FunctionSignature<'a> { + /// Get return type of this function. pub fn return_type(&self) -> Option { match self { &FunctionSignature::Module(ft) => ft.return_type(), @@ -660,6 +672,7 @@ impl<'a> FunctionSignature<'a> { } } + /// Get parameters of this function. pub fn params(&self) -> &[ValueType] { match self { &FunctionSignature::Module(ft) => ft.params(),