Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a single virtual machine for complex executions #617

Merged
merged 2 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benches/benches/bench_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mod benchmarks {
pub mod aoc_2020_1a;
pub mod aoc_2020_1b;
pub mod brainfuck;
pub mod external_functions;
pub mod fib;
}

Expand All @@ -59,4 +60,5 @@ criterion::criterion_main! {
benchmarks::aoc_2020_19b::benches,
benchmarks::brainfuck::benches,
benchmarks::fib::benches,
benchmarks::external_functions::benches,
}
36 changes: 36 additions & 0 deletions benches/benches/benchmarks/external_functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Benchmark external function calls.

use criterion::Criterion;

criterion::criterion_group!(benches, external_functions);

fn external_functions(b: &mut Criterion) {
let mut vm1 = rune_vm! {
fn a() {
79
}

fn b(f) {
f()
}

pub fn main() {
(a, b)
}
};

let mut vm2 = rune_vm! {
pub fn main(argument) {
let (a, b) = argument;
assert_eq!(b(a), 79);
}
};

let entry = rune::Hash::type_hash(["main"]);

b.bench_function("external_functions", |b| {
let output = vm1.call(entry, ()).expect("failed to fetch function");

b.iter(|| vm2.call(entry, (output.clone(),)).expect("failed call"));
});
}
2 changes: 1 addition & 1 deletion crates/rune/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ async fn do_trace<T>(
mut limit: usize,
) -> Result<Value, TraceError>
where
T: AsMut<Vm> + AsRef<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
let mut current_frame_len = execution.vm().call_frames().len();

Expand Down
31 changes: 19 additions & 12 deletions crates/rune/src/runtime/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,26 +823,33 @@ impl FnOffset {
///
/// This will cause a halt in case the vm being called into isn't the same
/// as the context and unit of the function.
#[tracing::instrument(skip_all, fields(args, extra = extra.count(), ?self.offset, ?self.call, ?self.args, ?self.hash))]
fn call_with_vm<E>(&self, vm: &mut Vm, args: usize, extra: E) -> VmResult<Option<VmCall>>
where
E: Args,
{
tracing::trace!("calling");

vm_try!(check_args(args, self.args));

// Fast past, just allocate a call frame and keep running.
if let Call::Immediate = self.call {
if vm.is_same(&self.context, &self.unit) {
vm_try!(vm.push_call_frame(self.offset, args));
vm_try!(extra.into_stack(vm.stack_mut()));
return VmResult::Ok(None);
}
let same_unit = matches!(self.call, Call::Immediate if vm.is_same_unit(&self.unit));
let same_context =
matches!(self.call, Call::Immediate if vm.is_same_context(&self.context));

vm_try!(vm.push_call_frame(self.offset, args, !same_context));
vm_try!(extra.into_stack(vm.stack_mut()));

// Fast path, just allocate a call frame and keep running.
if same_context && same_unit {
tracing::trace!("same context and unit");
return VmResult::Ok(None);
}

let mut new_stack = vm_try!(vm.stack_mut().drain(args)).collect::<Stack>();
vm_try!(extra.into_stack(&mut new_stack));
let mut vm = Vm::with_stack(self.context.clone(), self.unit.clone(), new_stack);
vm.set_ip(self.offset);
VmResult::Ok(Some(VmCall::new(self.call, vm)))
VmResult::Ok(Some(VmCall::new(
self.call,
(!same_context).then(|| self.context.clone()),
(!same_unit).then(|| self.unit.clone()),
)))
}
}

Expand Down
10 changes: 5 additions & 5 deletions crates/rune/src/runtime/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ use crate::runtime::{
/// A generator with a stored virtual machine.
pub struct Generator<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
execution: Option<VmExecution<T>>,
}

impl<T> Generator<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
/// Construct a generator from a virtual machine.
pub(crate) fn new(vm: T) -> Self {
Expand Down Expand Up @@ -108,7 +108,7 @@ impl iter::Iterator for GeneratorIterator {

impl<T> fmt::Debug for Generator<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Generator")
Expand All @@ -119,11 +119,11 @@ where

impl<T> Named for Generator<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
const BASE_NAME: RawStr = RawStr::from_str("Generator");
}

impl<T> InstallWith for Generator<T> where T: AsMut<Vm> {}
impl<T> InstallWith for Generator<T> where T: AsRef<Vm> + AsMut<Vm> {}

from_value!(Generator<Vm>, into_generator);
20 changes: 14 additions & 6 deletions crates/rune/src/runtime/stack.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::array;
use core::fmt;
use core::iter;
use core::mem;
use core::mem::replace;
use core::slice;

use crate::no_std::borrow::Cow;
Expand Down Expand Up @@ -111,6 +111,14 @@ impl Stack {
self.stack.len()
}

pub(crate) fn stack_size(&self) -> Result<usize, StackError> {
let Some(size) = self.stack.len().checked_sub(self.stack_bottom) else {
return Err(StackError);
};

Ok(size)
}

/// Perform a raw access over the stack.
///
/// This ignores [stack_bottom] and will just check that the given slice
Expand Down Expand Up @@ -345,9 +353,12 @@ impl Stack {
/// This is used internally when returning from a call frame.
///
/// Returns the old stack top.
#[tracing::instrument(skip_all)]
pub(crate) fn swap_stack_bottom(&mut self, count: usize) -> Result<usize, StackError> {
tracing::trace!(stack = ?self.stack.len(), self.stack_bottom, count);

match self.stack.len().checked_sub(count) {
Some(new_top) => Ok(mem::replace(&mut self.stack_bottom, new_top)),
Some(new_top) => Ok(replace(&mut self.stack_bottom, new_top)),
None => Err(StackError),
}
}
Expand All @@ -356,10 +367,7 @@ impl Stack {
// at the point of return.
#[tracing::instrument(skip_all)]
pub(crate) fn check_stack_top(&self) -> Result<(), StackError> {
tracing::trace!(
stack_len = self.stack.len(),
stack_bottom = self.stack_bottom,
);
tracing::trace!(stack = self.stack.len(), self.stack_bottom,);

if self.stack.len() == self.stack_bottom {
return Ok(());
Expand Down
10 changes: 5 additions & 5 deletions crates/rune/src/runtime/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use crate::runtime::{
/// A stream with a stored virtual machine.
pub struct Stream<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
execution: Option<VmExecution<T>>,
}

impl<T> Stream<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
/// Construct a stream from a virtual machine.
pub(crate) fn new(vm: T) -> Self {
Expand Down Expand Up @@ -83,16 +83,16 @@ impl Stream<&mut Vm> {

impl<T> Named for Stream<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
const BASE_NAME: RawStr = RawStr::from_str("Stream");
}

impl<T> InstallWith for Stream<T> where T: AsMut<Vm> {}
impl<T> InstallWith for Stream<T> where T: AsRef<Vm> + AsMut<Vm> {}

impl<T> fmt::Debug for Stream<T>
where
T: AsMut<Vm>,
T: AsRef<Vm> + AsMut<Vm>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Stream")
Expand Down
Loading