Skip to content

Commit

Permalink
Opcode dispatch & variable set/get optimization
Browse files Browse the repository at this point in the history
  * 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.
  • Loading branch information
rdaum committed Jan 7, 2024
1 parent 1c51aa7 commit ee10888
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 72 deletions.
23 changes: 3 additions & 20 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ itertools = "0.12.0"
lazy_static = "1.4.0"
num-traits = "0.2.17"
rustyline = "13.0.0"
sized-chunks = "0.7.0"
strum = { version = "0.25.0", features = ["derive"] }
uuid = { version = "1.6.1", features = ["v4"] }
yoke = "0.7.3"
Expand Down
1 change: 0 additions & 1 deletion crates/db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ metrics-macros.workspace = true
# For the DB layer.
bincode.workspace = true
im.workspace = true
sized-chunks.workspace = true
binary-layout.workspace = true
libc.workspace = true
atomic-wait.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/db/src/tuplebox/tuples/slotbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl SlotBox {

pub fn num_pages(&self) -> usize {
let mut inner = self.inner.lock().unwrap();
inner.available_page_space.size()
inner.available_page_space.len()
}

pub fn used_pages(&self) -> Vec<PageId> {
Expand Down
3 changes: 1 addition & 2 deletions crates/kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ async-trait.workspace = true
decorum.workspace = true
text_io.workspace = true
bytes.workspace = true
strum.workspace = true
strum.worksspace = true
uuid.workspace = true
chrono.workspace = true
sized-chunks.workspace = true

## Required for MOO builtins.
pwhash.workspace = true
Expand Down
55 changes: 31 additions & 24 deletions crates/kernel/src/vm/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
//

use moor_values::NOTHING;
use sized_chunks::SparseChunk;
use tracing::trace;
use uuid::Uuid;

use moor_compiler::GlobalName;
Expand All @@ -36,6 +34,7 @@ use crate::tasks::TaskId;
use crate::vm::VerbExecutionRequest;
use moor_compiler::labels::{Label, Name};
use moor_compiler::opcode::{Op, Program, EMPTY_PROGRAM};
use moor_values::util::{BitArray, Bitset32};

// {this, verb-name, programmer, verb-loc, player, line-number}
#[derive(Clone)]
Expand Down Expand Up @@ -92,7 +91,7 @@ pub(crate) struct Activation {
/// and caller_perms() returns the value of this in the *parent* stack frame (or #-1 if none)
pub(crate) permissions: Objid,
/// The values of the variables currently in scope, by their offset.
pub(crate) environment: SparseChunk<Var, 256>,
pub(crate) environment: BitArray<Var, 256, Bitset32<8>>,
/// The value stack.
pub(crate) valstack: Vec<Var>,
/// A stack of active error handlers, each relative to a position in the valstack.
Expand Down Expand Up @@ -126,7 +125,7 @@ fn set_constants(a: &mut Activation) {
impl Activation {
pub fn for_call(task_id: TaskId, verb_call_request: VerbExecutionRequest) -> Self {
let program = verb_call_request.program;
let environment = SparseChunk::new();
let environment = BitArray::new();

let verb_owner = verb_call_request.resolved_verb.verbdef().owner();
let mut a = Self {
Expand Down Expand Up @@ -183,7 +182,7 @@ impl Activation {
}

pub fn for_eval(task_id: TaskId, permissions: Objid, player: Objid, program: Program) -> Self {
let environment = SparseChunk::new();
let environment = BitArray::new();

let verb_info = VerbInfo::new(
// Fake verbdef. Not sure how I feel about this. Similar to with BF calls.
Expand Down Expand Up @@ -256,11 +255,10 @@ impl Activation {
SliceRef::empty(),
);

trace!(bf_name, bf_index, ?args, "for_bf_call");
Self {
task_id,
program: EMPTY_PROGRAM.clone(),
environment: SparseChunk::new(),
environment: BitArray::new(),
valstack: vec![],
handler_stack: vec![],
pc: 0,
Expand Down Expand Up @@ -306,18 +304,21 @@ impl Activation {
self.verb_info.verbdef().owner()
}

#[inline]
pub fn set_gvar(&mut self, gname: GlobalName, value: Var) {
self.environment.insert(gname as usize, value);
self.environment.set(gname as usize, value);
}

#[inline]
pub fn set_var_offset(&mut self, offset: Name, value: Var) -> Result<(), Error> {
if offset.0 as usize >= self.environment.len() {
return Err(E_VARNF);
}
self.environment[offset.0 as usize] = value;
self.environment.set(offset.0 as usize, value);
Ok(())
}

#[inline]
pub fn next_op(&mut self) -> Option<Op> {
if !self.pc < self.program.main_vector.len() {
return None;
Expand All @@ -327,22 +328,43 @@ impl Activation {
Some(op)
}

#[inline]
pub fn lookahead(&self) -> Option<Op> {
self.program.main_vector.get(self.pc).cloned()
}

#[inline]
pub fn skip(&mut self) {
self.pc += 1;
}

#[inline]
pub fn pop(&mut self) -> Option<Var> {
self.valstack.pop()
}

#[inline]
pub fn push(&mut self, v: Var) {
self.valstack.push(v)
}

#[inline]
pub fn peek_top(&self) -> Option<Var> {
self.valstack.last().cloned()
}

#[inline]
pub fn peek(&self, width: usize) -> Vec<Var> {
let l = self.valstack.len();
Vec::from(&self.valstack[l - width..])
}

#[inline]
pub fn jump(&mut self, label_id: Label) {
let label = &self.program.jump_labels[label_id.0 as usize];
self.pc = label.position.0;
}

pub fn push_handler_label(&mut self, handler_type: HandlerType) {
self.handler_stack.push(HandlerLabel {
handler_type,
Expand All @@ -359,19 +381,4 @@ impl Activation {
}
self.handler_stack.pop()
}

pub fn peek_top(&self) -> Option<Var> {
self.valstack.last().cloned()
}

pub fn peek(&self, width: usize) -> Vec<Var> {
let l = self.valstack.len();
Vec::from(&self.valstack[l - width..])
}

pub fn jump(&mut self, label_id: Label) {
let label = &self.program.jump_labels[label_id.0 as usize];
trace!("Jump to {}", label.position.0);
self.pc = label.position.0;
}
}
12 changes: 11 additions & 1 deletion crates/kernel/src/vm/exec_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ impl VMExecState {
callers
}

#[inline]
pub(crate) fn top_mut(&mut self) -> &mut Activation {
self.stack.last_mut().expect("activation stack underflow")
}

#[inline]
pub(crate) fn top(&self) -> &Activation {
self.stack.last().expect("activation stack underflow")
}
Expand Down Expand Up @@ -132,6 +134,7 @@ impl VMExecState {
}

/// Pop a value off the value stack.
#[inline]
pub(crate) fn pop(&mut self) -> Var {
self.top_mut().pop().unwrap_or_else(|| {
panic!(
Expand All @@ -143,37 +146,44 @@ impl VMExecState {
}

/// Push a value onto the value stack
#[inline]
pub(crate) fn push(&mut self, v: &Var) {
self.top_mut().push(v.clone())
}

/// Non-destructively peek in the value stack at the given offset.
#[inline]
pub(crate) fn peek(&self, amt: usize) -> Vec<Var> {
self.top().peek(amt)
}

/// Return the top of the value stack.
#[inline]
pub(crate) fn peek_top(&self) -> Var {
self.top().peek_top().expect("stack underflow")
}

/// Return the next opcode in the program stream.
#[inline]
pub(crate) fn next_op(&mut self) -> Option<Op> {
self.top_mut().next_op()
}

/// Jump to the given label.
#[inline]
pub(crate) fn jump(&mut self, label: Label) {
self.top_mut().jump(label)
}

/// Return the value of a local variable.
#[inline]
pub(crate) fn get_env(&self, id: Name) -> Option<&Var> {
self.top().environment.get(id.0 as usize)
}

/// Set the value of a local variable.
#[inline]
pub(crate) fn set_env(&mut self, id: Name, v: &Var) {
self.top_mut().environment.insert(id.0 as usize, v.clone());
self.top_mut().environment.set(id.0 as usize, v.clone());
}
}
19 changes: 4 additions & 15 deletions crates/kernel/src/vm/vm_execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use std::time::Duration;
use tokio::sync::mpsc::UnboundedSender;

use moor_compiler::labels::{Name, Offset};
use tracing::trace;

use crate::tasks::command_parse::ParsedCommand;
use crate::tasks::sessions::Session;
Expand Down Expand Up @@ -192,16 +191,6 @@ impl VM {

state.tick_count += 1;

trace!(
pc = state.top().pc,
?op,
this = ?state.top().this,
player = ?state.top().player,
stack = ?state.top().valstack,
tick_count = state.tick_count,
tick_slice,
"exec"
);
match op {
Op::If(label) | Op::Eif(label) | Op::IfQues(label) | Op::While(label) => {
let cond = state.pop();
Expand Down Expand Up @@ -263,13 +252,13 @@ impl VM {
} => {
// 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.
// 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());

// 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
// we also don't want to *crash* on obscene values, so impl that here.
// Given we're 64-bit this is highly unlikely to ever be a concern for us, but
// we also don't want to *crash* on obscene values, so impl that here.

let next_val = match (to.variant(), from.variant()) {
(Variant::Int(to_i), Variant::Int(from_i)) => {
Expand Down
Loading

0 comments on commit ee10888

Please sign in to comment.