summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2024-02-01 01:36:12 +0100
committerHenry Gressmann <mail@henrygressmann.de>2024-02-01 01:36:12 +0100
commitf6dba823b785cb05a024d52f787b81bd56d6b3e4 (patch)
tree3cede77d20223d6d1991a941e55f0ab1ccacc861 /crates
parent6850e8bc669171c658696655f4f31996d6e934af (diff)
perf: small performance improvements
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/instance.rs20
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs3
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs22
-rw-r--r--crates/tinywasm/src/store/memory.rs3
-rw-r--r--crates/tinywasm/src/store/mod.rs120
5 files changed, 100 insertions, 68 deletions
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index 75bd20d..6bfe99c 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -36,15 +36,18 @@ pub(crate) struct ModuleInstanceInner {
impl ModuleInstance {
// drop the module instance reference and swap it with another one
+ #[inline]
pub(crate) fn swap(&mut self, other: Self) {
self.0 = other.0;
}
+ #[inline]
pub(crate) fn swap_with(&mut self, other_addr: ModuleInstanceAddr, store: &mut Store) {
self.swap(store.get_module_instance_raw(other_addr))
}
/// Get the module instance's address
+ #[inline]
pub fn id(&self) -> ModuleInstanceAddr {
self.0.idx
}
@@ -118,44 +121,53 @@ impl ModuleInstance {
Some(ExternVal::new(kind, *addr))
}
- pub(crate) fn func_addrs(&self) -> &[FuncAddr] {
- &self.0.func_addrs
- }
-
+ #[inline]
pub(crate) fn new(inner: ModuleInstanceInner) -> Self {
Self(Rc::new(inner))
}
+ #[inline]
pub(crate) fn func_ty(&self, addr: FuncAddr) -> &FuncType {
self.0.types.get(addr as usize).expect("No func type for func, this is a bug")
}
+ #[inline]
+ pub(crate) fn func_addrs(&self) -> &[FuncAddr] {
+ &self.0.func_addrs
+ }
+
// resolve a function address to the global store address
+ #[inline]
pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> FuncAddr {
*self.0.func_addrs.get(addr as usize).expect("No func addr for func, this is a bug")
}
// resolve a table address to the global store address
+ #[inline]
pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> TableAddr {
*self.0.table_addrs.get(addr as usize).expect("No table addr for table, this is a bug")
}
// resolve a memory address to the global store address
+ #[inline]
pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> MemAddr {
*self.0.mem_addrs.get(addr as usize).expect("No mem addr for mem, this is a bug")
}
// resolve a data address to the global store address
+ #[inline]
pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> MemAddr {
*self.0.data_addrs.get(addr as usize).expect("No data addr for data, this is a bug")
}
// resolve a memory address to the global store address
+ #[inline]
pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr {
*self.0.elem_addrs.get(addr as usize).expect("No elem addr for elem, this is a bug")
}
// resolve a global address to the global store address
+ #[inline]
pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> GlobalAddr {
self.0.global_addrs[addr as usize]
}
diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs
index 9333a4c..d6c0553 100644
--- a/crates/tinywasm/src/runtime/interpreter/macros.rs
+++ b/crates/tinywasm/src/runtime/interpreter/macros.rs
@@ -28,13 +28,13 @@ macro_rules! mem_load {
}};
($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{
- // TODO: there could be a lot of performance improvements here
let mem_idx = $module.resolve_mem_addr($arg.mem_addr);
let mem = $store.get_mem(mem_idx as usize)?;
let mem_ref = mem.borrow_mut();
let addr = $stack.values.pop()?.raw_value();
let addr = $arg.offset.checked_add(addr).ok_or_else(|| {
+ cold();
Error::Trap(crate::Trap::MemoryOutOfBounds {
offset: $arg.offset as usize,
len: core::mem::size_of::<$load_type>(),
@@ -43,6 +43,7 @@ macro_rules! mem_load {
})?;
let addr: usize = addr.try_into().ok().ok_or_else(|| {
+ cold();
Error::Trap(crate::Trap::MemoryOutOfBounds {
offset: $arg.offset as usize,
len: core::mem::size_of::<$load_type>(),
diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs
index c8d8791..7e34ac1 100644
--- a/crates/tinywasm/src/runtime/interpreter/mod.rs
+++ b/crates/tinywasm/src/runtime/interpreter/mod.rs
@@ -29,28 +29,31 @@ impl InterpreterRuntime {
let mut current_module = store.get_module_instance_raw(cf.func_instance.1);
loop {
- match exec_one(&mut cf, stack, store, &current_module)? {
+ match exec_one(&mut cf, stack, store, &current_module) {
// Continue execution at the new top of the call stack
- ExecResult::Call => {
+ Ok(ExecResult::Call) => {
cf = stack.call_stack.pop()?;
+
+ // keeping the pointer seperate from the call frame is about 2% faster
+ // than storing it in the call frame
if cf.func_instance.1 != current_module.id() {
current_module.swap_with(cf.func_instance.1, store);
}
}
// return from the function
- ExecResult::Return => return Ok(()),
+ Ok(ExecResult::Return) => return Ok(()),
// continue to the next instruction and increment the instruction pointer
- ExecResult::Ok => cf.instr_ptr += 1,
+ Ok(ExecResult::Ok) => cf.instr_ptr += 1,
// trap the program
- ExecResult::Trap(trap) => {
+ Err(error) => {
cf.instr_ptr += 1;
// push the call frame back onto the stack so that it can be resumed
// if the trap can be handled
stack.call_stack.push(cf)?;
- return Err(Error::Trap(trap));
+ return Err(error);
}
}
}
@@ -61,13 +64,14 @@ enum ExecResult {
Ok,
Return,
Call,
- Trap(crate::Trap),
}
/// Run a single step of the interpreter
/// A seperate function is used so later, we can more easily implement
/// a step-by-step debugger (using generators once they're stable?)
-#[inline(always)] // this improves performance by more than 20% in some cases
+// we want this be always part of the loop, rust just doesn't inline it as its too big
+// this can be a 30%+ performance difference in some cases
+#[inline(always)]
fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result<ExecResult> {
let instrs = &cf.func_instance.0.instructions;
if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) {
@@ -84,7 +88,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
Nop => { /* do nothing */ }
Unreachable => {
cold();
- return Ok(ExecResult::Trap(crate::Trap::Unreachable));
+ return Err(crate::Trap::Unreachable.into());
} // we don't need to include the call frame here because it's already on the stack
Drop => stack.values.pop().map(|_| ())?,
diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs
index 64e16c6..588d995 100644
--- a/crates/tinywasm/src/store/memory.rs
+++ b/crates/tinywasm/src/store/memory.rs
@@ -100,6 +100,7 @@ impl MemoryInstance {
Ok(val)
}
+ #[inline]
pub(crate) fn page_count(&self) -> usize {
self.page_count
}
@@ -186,10 +187,12 @@ macro_rules! impl_mem_loadable_for_primitive {
$(
#[allow(unsafe_code)]
unsafe impl MemLoadable<$size> for $type {
+ #[inline]
fn from_le_bytes(bytes: [u8; $size]) -> Self {
<$type>::from_le_bytes(bytes)
}
+ #[inline]
fn from_be_bytes(bytes: [u8; $size]) -> Self {
<$type>::from_be_bytes(bytes)
}
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index ad24480..ce02dd7 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -116,6 +116,72 @@ impl Store {
Ok(())
}
+ #[cold]
+ fn not_found_error(name: &str) -> Error {
+ Error::Other(format!("{} not found", name))
+ }
+
+ /// Get the function at the actual index in the store
+ #[inline]
+ pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> {
+ self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function"))
+ }
+
+ /// Get the memory at the actual index in the store
+ #[inline]
+ pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc<RefCell<MemoryInstance>>> {
+ self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory"))
+ }
+
+ /// Get the table at the actual index in the store
+ #[inline]
+ pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc<RefCell<TableInstance>>> {
+ self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table"))
+ }
+
+ /// Get the data at the actual index in the store
+ #[inline]
+ pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> {
+ self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data"))
+ }
+
+ /// Get the data at the actual index in the store
+ #[inline]
+ pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> {
+ self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data"))
+ }
+
+ /// Get the element at the actual index in the store
+ #[inline]
+ pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> {
+ self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element"))
+ }
+
+ /// Get the global at the actual index in the store
+ #[inline]
+ pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc<RefCell<GlobalInstance>>> {
+ self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global"))
+ }
+
+ /// Get the global at the actual index in the store
+ #[inline]
+ pub fn get_global_val(&self, addr: usize) -> Result<RawWasmValue> {
+ self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value)
+ }
+
+ /// Set the global at the actual index in the store
+ #[inline]
+ pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> {
+ self.data
+ .globals
+ .get(addr)
+ .ok_or_else(|| Self::not_found_error("global"))
+ .map(|global| global.borrow_mut().value = value)
+ }
+}
+
+// Linking related functions
+impl Store {
/// Add functions to the store, returning their addresses in the store
pub(crate) fn init_funcs(
&mut self,
@@ -391,58 +457,4 @@ impl Store {
};
Ok(val)
}
-
- #[cold]
- fn not_found_error(name: &str) -> Error {
- Error::Other(format!("{} not found", name))
- }
-
- /// Get the function at the actual index in the store
- pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> {
- self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function"))
- }
-
- /// Get the memory at the actual index in the store
- pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc<RefCell<MemoryInstance>>> {
- self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory"))
- }
-
- /// Get the table at the actual index in the store
- pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc<RefCell<TableInstance>>> {
- self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table"))
- }
-
- /// Get the data at the actual index in the store
- pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> {
- self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data"))
- }
-
- /// Get the data at the actual index in the store
- pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> {
- self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data"))
- }
-
- /// Get the element at the actual index in the store
- pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> {
- self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element"))
- }
-
- /// Get the global at the actual index in the store
- pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc<RefCell<GlobalInstance>>> {
- self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global"))
- }
-
- /// Get the global at the actual index in the store
- pub fn get_global_val(&self, addr: usize) -> Result<RawWasmValue> {
- self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value)
- }
-
- /// Set the global at the actual index in the store
- pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> {
- self.data
- .globals
- .get(addr)
- .ok_or_else(|| Self::not_found_error("global"))
- .map(|global| global.borrow_mut().value = value)
- }
}