From 554e1bd00c217fb7e9c98ae00259100ef4b6dda6 Mon Sep 17 00:00:00 2001 From: Ryan Daum Date: Sat, 6 Jan 2024 21:54:14 -0500 Subject: [PATCH] Opcode dispatch & variable set/get optimization * Replace SparseChunk with BitArray; minor improvement * Inline a bunch of tight-loopedfunctions: +95% improvement in opcode execution throughput(!) * Drop some trace! stuff for a ~5% improvement. * "Peek" optimization (as in LambdaMOO) in the valstack for for-range. +49% throughput improvement(!) --- crates/kernel/src/vm/activation.rs | 13 +++++++++++++ crates/kernel/src/vm/exec_state.rs | 12 ++++++++++++ crates/kernel/src/vm/vm_execute.rs | 19 ++++++++++++------- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/crates/kernel/src/vm/activation.rs b/crates/kernel/src/vm/activation.rs index 75eba37e..f0ee04f9 100644 --- a/crates/kernel/src/vm/activation.rs +++ b/crates/kernel/src/vm/activation.rs @@ -359,6 +359,19 @@ impl Activation { Vec::from(&self.valstack[l - width..]) } + #[inline] + pub fn peek2(&self) -> (Var, Var) { + let l = self.valstack.len(); + let (a, b) = (&self.valstack[l - 1], &self.valstack[l - 2]); + (a.clone(), b.clone()) + } + + #[inline] + pub fn update(&mut self, amt: usize, v: Var) { + let l = self.valstack.len(); + self.valstack[l - amt - 1] = v; + } + #[inline] pub fn jump(&mut self, label_id: Label) { let label = &self.program.jump_labels[label_id.0 as usize]; diff --git a/crates/kernel/src/vm/exec_state.rs b/crates/kernel/src/vm/exec_state.rs index f59b3425..6b478e12 100644 --- a/crates/kernel/src/vm/exec_state.rs +++ b/crates/kernel/src/vm/exec_state.rs @@ -157,6 +157,18 @@ impl VMExecState { self.top().peek(amt) } + /// Return the top two values on the value stack. + #[inline] + pub(crate) fn peek2(&self) -> (Var, Var) { + self.top().peek2() + } + + /// Update at a set offset in the value stack. + #[inline] + pub(crate) fn update(&mut self, amt: usize, v: &Var) { + self.top_mut().update(amt, v.clone()) + } + /// Return the top of the value stack. #[inline] pub(crate) fn peek_top(&self) -> Var { diff --git a/crates/kernel/src/vm/vm_execute.rs b/crates/kernel/src/vm/vm_execute.rs index 5aa13757..35ceddcf 100644 --- a/crates/kernel/src/vm/vm_execute.rs +++ b/crates/kernel/src/vm/vm_execute.rs @@ -251,10 +251,7 @@ impl VM { id, } => { // Pull the range ends off the stack. - // TODO LambdaMOO had optimization here where it would only peek and update. - // But I had some difficulty getting stack values right, so will do this simpler - // for now and revisit later. - let (to, from) = (&state.pop(), &state.pop()); + let (to, from) = state.peek2(); // TODO: LambdaMOO has special handling for MAXINT/MAXOBJ // Given we're 64-bit this is highly unlikely to ever be a concern for us, but @@ -263,30 +260,38 @@ impl VM { let next_val = match (to.variant(), from.variant()) { (Variant::Int(to_i), Variant::Int(from_i)) => { if from_i > to_i { + state.pop(); + state.pop(); state.jump(label); + continue; } v_int(from_i + 1) } (Variant::Obj(to_o), Variant::Obj(from_o)) => { if from_o.0 > to_o.0 { + state.pop(); + state.pop(); state.jump(label); + continue; } v_obj(from_o.0 + 1) } (_, _) => { + state.pop(); + state.pop(); // Make sure we've jumped out of the loop before raising the error, // because in verbs that aren't `d' we could end up continuing on in // the loop (with a messed up stack) otherwise. state.jump(label); + return self.raise_error(state, E_TYPE); } }; - state.set_env(id, from); - state.push(&next_val); - state.push(to); + state.update(1, &next_val); + state.set_env(id, &from); } Op::Pop => { state.pop();