Skip to content

Commit 19514c8

Browse files
committed
perf: keep the instruction slice in a local in the match dispatch loop
The match loop spilled the executor's function and reloaded it, then the instruction slice's pointer and length, on every step. The executing function is borrowed from the module instance, not from the executor, so the loop can hold its instructions in a local and reload them only when a step switches functions.
1 parent 2bdebab commit 19514c8

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

‎crates/tinywasm/src/interpreter/executor/dispatch.rs‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ macro_rules! define_stable_dispatch {
44
($executor:ident, $instr_ptr:ident, $dispatch_next:ident, $dispatch_flow:ident;
55
$($variant:ident $(($($arg:pat),*))? $({ $($field:ident),* })? => $body:expr),* $(,)?) => {
66
#[inline(always)]
7-
fn exec_step($executor: &mut Self, $instr_ptr: usize) -> ExecResult<ExecFlow> {
7+
fn exec_step(
8+
$executor: &mut Self,
9+
instructions: &[tinywasm_types::Instruction],
10+
$instr_ptr: usize,
11+
) -> ExecResult<ExecFlow> {
812
macro_rules! $dispatch_next {
913
($next_instr_ptr:expr) => {{
1014
return Ok(ExecFlow::Next($next_instr_ptr));
@@ -16,23 +20,49 @@ macro_rules! define_stable_dispatch {
1620
}};
1721
}
1822
use tinywasm_types::Instruction::*;
19-
match &$executor.func.instructions[$instr_ptr] {
23+
match &instructions[$instr_ptr] {
2024
$($variant $(($($arg),*))? $({ $($field),* })? => $body,)*
2125
}
2226
Ok(ExecFlow::Next($instr_ptr + 1))
2327
}
2428
};
2529
}
2630

27-
impl Executor<'_, '_> {
31+
impl<'module> Executor<'_, 'module> {
2832
instruction_handlers!(define_stable_dispatch);
2933

34+
/// The executing function's instructions. They are borrowed from the module instance, not from
35+
/// the executor, so the dispatch loop keeps them in a local across steps.
36+
#[inline(always)]
37+
fn instructions(&self) -> &'module [tinywasm_types::Instruction] {
38+
let func: &'module WasmFunction = self.func;
39+
&func.instructions
40+
}
41+
42+
/// One step from `instr_ptr`. A step that switches functions reloads `instructions`.
43+
#[inline(always)]
44+
fn step(
45+
&mut self,
46+
instructions: &mut &'module [tinywasm_types::Instruction],
47+
instr_ptr: usize,
48+
) -> ExecResult<Option<usize>> {
49+
Ok(match Self::exec_step(self, instructions, instr_ptr)? {
50+
ExecFlow::Next(next_instr_ptr) => Some(next_instr_ptr),
51+
ExecFlow::Switch(next_instr_ptr) => {
52+
*instructions = self.instructions();
53+
Some(next_instr_ptr)
54+
}
55+
ExecFlow::Complete => None,
56+
})
57+
}
58+
3059
/// Runs until the call completes (`None`) or continues in another module instance's frame.
3160
#[inline(always)]
3261
pub(crate) fn run_to_completion(mut self) -> Result<Option<CallFrame>> {
3362
let mut instr_ptr = self.cf.instr_ptr;
63+
let mut instructions = self.instructions();
3464
loop {
35-
match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
65+
match self.step(&mut instructions, instr_ptr)? {
3666
Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
3767
None => return cold!(Ok(self.left())),
3868
}
@@ -50,10 +80,11 @@ impl Executor<'_, '_> {
5080
mut chunk_left: u32,
5181
) -> Result<RunEnd> {
5282
let mut instr_ptr = self.cf.instr_ptr;
83+
let mut instructions = self.instructions();
5384
loop {
5485
while chunk_left != 0 {
5586
chunk_left -= 1;
56-
match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
87+
match self.step(&mut instructions, instr_ptr)? {
5788
Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
5889
None => return Ok(self.run_end(chunk_left)),
5990
}
@@ -72,10 +103,11 @@ impl Executor<'_, '_> {
72103
pub(crate) fn run_with_fuel(mut self, mut chunk_left: u32) -> Result<RunEnd> {
73104
self.fuel_metered = true;
74105
let mut instr_ptr = self.cf.instr_ptr;
106+
let mut instructions = self.instructions();
75107
loop {
76108
while chunk_left != 0 {
77109
chunk_left -= 1;
78-
match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
110+
match self.step(&mut instructions, instr_ptr)? {
79111
Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
80112
None => return Ok(self.run_end(chunk_left)),
81113
}

‎crates/tinywasm/src/interpreter/executor/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ enum ExecFlow {
6262
}
6363

6464
impl ExecFlow {
65+
#[cfg(feature = "nightly-tail-calls")]
6566
#[inline(always)]
6667
fn next_instr_ptr(self) -> Option<usize> {
6768
match self {

0 commit comments

Comments
 (0)