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

Optimize bare call overhead #522

Merged
merged 9 commits into from
Oct 18, 2022
36 changes: 5 additions & 31 deletions crates/wasmi/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,23 +285,19 @@ impl EngineInner {
Results: CallResults,
{
self.initialize_args(params);
let signature = match func.as_internal(ctx.as_context()) {
match func.as_internal(ctx.as_context()) {
FuncEntityInternal::Wasm(wasm_func) => {
let signature = wasm_func.signature();
let mut frame = self.stack.call_wasm_root(wasm_func, &self.code_map)?;
let mut cache = InstanceCache::from(frame.instance());
self.execute_wasm_func(ctx.as_context_mut(), &mut frame, &mut cache)?;
signature
}
FuncEntityInternal::Host(host_func) => {
let signature = host_func.signature();
let host_func = host_func.clone();
self.stack
.call_host_root(ctx.as_context_mut(), host_func, &self.func_types)?;
signature
}
};
let results = self.write_results_back(signature, results);
let results = self.write_results_back(results);
Ok(results)
}

Expand All @@ -311,9 +307,7 @@ impl EngineInner {
Params: CallParams,
{
self.stack.clear();
for param in params.feed_params() {
self.stack.values.push(param);
}
self.stack.values.extend(params.call_params());
}

/// Writes the results of the function execution back into the `results` buffer.
Expand All @@ -325,31 +319,11 @@ impl EngineInner {
/// # Panics
///
/// - If the `results` buffer length does not match the remaining amount of stack values.
fn write_results_back<Results>(
&mut self,
func_type: DedupFuncType,
results: Results,
) -> <Results as CallResults>::Results
fn write_results_back<Results>(&mut self, results: Results) -> <Results as CallResults>::Results
where
Results: CallResults,
{
let result_types = self.func_types.resolve_func_type(func_type).results();
assert_eq!(
self.stack.values.len(),
results.len_results(),
"expected {} values on the stack after function execution but found {}",
results.len_results(),
self.stack.values.len(),
);
assert_eq!(results.len_results(), result_types.len());
results.feed_results(
self.stack
.values
.drain()
.iter()
.zip(result_types)
.map(|(raw_value, value_type)| raw_value.with_type(*value_type)),
)
results.call_results(self.stack.values.drain())
}

/// Executes the top most Wasm function on the [`Stack`] until the [`Stack`] is empty.
Expand Down
78 changes: 35 additions & 43 deletions crates/wasmi/src/engine/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::core::Value;
use core::{iter, slice};
use wasmi_core::UntypedValue;

/// Types implementing this trait may be used as parameters for function execution.
///
Expand All @@ -12,33 +13,45 @@ use core::{iter, slice};
/// [`Engine`]: [`crate::Engine`]
pub trait CallParams {
/// The iterator over the parameter values.
type Params: Iterator<Item = Value>;

/// Returns the number of given parameter values.
///
/// # Note
///
/// Used by the [`Engine`] to determine how many parameters are received.
///
/// [`Engine`]: [`crate::Engine`]
fn len_params(&self) -> usize;
type Params: ExactSizeIterator<Item = UntypedValue>;

/// Feeds the parameter values from the caller.
fn feed_params(self) -> Self::Params;
fn call_params(self) -> Self::Params;
}

impl<'a> CallParams for &'a [Value] {
type Params = iter::Copied<slice::Iter<'a, Value>>;
type Params = CallParamsValueIter<'a>;

fn len_params(&self) -> usize {
self.len()
#[inline]
fn call_params(self) -> Self::Params {
CallParamsValueIter {
iter: self.iter().copied(),
}
}
}

/// An iterator over the [`UntypedValue`] call parameters.
#[derive(Debug)]
pub struct CallParamsValueIter<'a> {
iter: iter::Copied<slice::Iter<'a, Value>>,
}

fn feed_params(self) -> Self::Params {
self.iter().copied()
impl<'a> Iterator for CallParamsValueIter<'a> {
type Item = UntypedValue;

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(UntypedValue::from)
}
}

impl ExactSizeIterator for CallParamsValueIter<'_> {}

/// Types implementing this trait may be used as results for function execution.
///
/// # Note
Expand All @@ -53,43 +66,22 @@ pub trait CallResults {
/// The type of the returned results value.
type Results;

/// Returns the number of expected result values.
///
/// # Note
///
/// Used by the [`Engine`] to determine how many results are expected.
///
/// [`Engine`]: [`crate::Engine`]
fn len_results(&self) -> usize;

/// Feeds the result values back to the caller.
///
/// # Panics
///
/// If the given `results` do not match the expected amount.
fn feed_results<T>(self, results: T) -> Self::Results
where
T: IntoIterator<Item = Value>,
T::IntoIter: ExactSizeIterator;
fn call_results(self, results: &[UntypedValue]) -> Self::Results;
}

impl<'a> CallResults for &'a mut [Value] {
type Results = Self;

fn len_results(&self) -> usize {
self.len()
}

fn feed_results<T>(self, results: T) -> Self::Results
where
T: IntoIterator<Item = Value>,
T::IntoIter: ExactSizeIterator,
{
let results = results.into_iter();
assert_eq!(self.len_results(), results.len());
for (dst, src) in self.iter_mut().zip(results) {
*dst = src;
}
fn call_results(self, results: &[UntypedValue]) -> Self::Results {
assert_eq!(self.len(), results.len());
self.iter_mut().zip(results).for_each(|(dst, src)| {
*dst = src.with_type(dst.value_type());
});
self
}
}
84 changes: 38 additions & 46 deletions crates/wasmi/src/func/into_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ macro_rules! impl_into_func {
#[allow(non_snake_case)]
fn into_func(self) -> (FuncType, HostFuncTrampoline<T>) {
let signature = FuncType::new(
<Self::Params as WasmTypeList>::value_types(),
<Self::Results as WasmTypeList>::value_types(),
<Self::Params as WasmTypeList>::types(),
<Self::Results as WasmTypeList>::types(),
);
let trampoline = HostFuncTrampoline::new(
move |caller: Caller<T>, params_results: FuncParams| -> Result<FuncResults, Trap> {
Expand Down Expand Up @@ -197,31 +197,32 @@ pub trait WasmTypeList: DecodeUntypedSlice + EncodeUntypedSlice + Sized {
/// The [`ValueType`] sequence as array.
type Types: IntoIterator<IntoIter = Self::TypesIter, Item = ValueType>
+ AsRef<[ValueType]>
+ AsMut<[ValueType]>;
+ AsMut<[ValueType]>
+ Copy
+ Clone;

/// The iterator type of the sequence of [`ValueType`].
type TypesIter: ExactSizeIterator<Item = ValueType> + DoubleEndedIterator + FusedIterator;

/// The [`Value`] sequence as array.
type Values: IntoIterator<IntoIter = Self::ValuesIter, Item = Value>
+ AsRef<[Value]>
+ AsMut<[Value]>;
type Values: IntoIterator<IntoIter = Self::ValuesIter, Item = UntypedValue>
+ AsRef<[UntypedValue]>
+ AsMut<[UntypedValue]>
+ Copy
+ Clone;

/// The iterator type of the sequence of [`Value`].
type ValuesIter: ExactSizeIterator<Item = Value> + DoubleEndedIterator + FusedIterator;
type ValuesIter: ExactSizeIterator<Item = UntypedValue> + DoubleEndedIterator + FusedIterator;

/// Returns an array representing the [`ValueType`] sequence of `Self`.
fn value_types() -> Self::Types;
fn types() -> Self::Types;

/// Returns an array representing the [`Value`] sequence of `self`.
/// Returns an array representing the [`UntypedValue`] sequence of `self`.
fn values(self) -> Self::Values;

/// Consumes the [`Value`] iterator and creates `Self` if possible.
/// Consumes the [`UntypedValue`] iterator and creates `Self` if possible.
///
/// Returns `None` if construction of `Self` is impossible.
fn from_values<T>(values: T) -> Option<Self>
where
T: Iterator<Item = Value>;
fn from_values(values: &[UntypedValue]) -> Option<Self>;
}

impl<T1> WasmTypeList for T1
Expand All @@ -232,31 +233,25 @@ where

type Types = [ValueType; 1];
type TypesIter = array::IntoIter<ValueType, 1>;
type Values = [Value; 1];
type ValuesIter = array::IntoIter<Value, 1>;
type Values = [UntypedValue; 1];
type ValuesIter = array::IntoIter<UntypedValue, 1>;

#[inline]
fn value_types() -> Self::Types {
fn types() -> Self::Types {
[<T1 as WasmType>::value_type()]
}

#[inline]
fn values(self) -> Self::Values {
[<T1 as Into<Value>>::into(self)]
[<T1 as Into<UntypedValue>>::into(self)]
}

fn from_values<T>(mut values: T) -> Option<Self>
where
T: Iterator<Item = Value>,
{
let value: T1 = values.next().and_then(Value::try_into)?;
if values.next().is_some() {
// Note: If the iterator yielded more items than
// necessary we create no value from this procedure
// as it is likely a bug.
return None;
#[inline]
fn from_values(values: &[UntypedValue]) -> Option<Self> {
if let [value] = *values {
return Some(value.into());
}
Some(value)
None
}
}

Expand All @@ -272,37 +267,34 @@ macro_rules! impl_wasm_type_list {

type Types = [ValueType; $n];
type TypesIter = array::IntoIter<ValueType, $n>;
type Values = [Value; $n];
type ValuesIter = array::IntoIter<Value, $n>;
type Values = [UntypedValue; $n];
type ValuesIter = array::IntoIter<UntypedValue, $n>;

fn value_types() -> Self::Types {
#[inline]
fn types() -> Self::Types {
[$(
<$tuple as WasmType>::value_type()
),*]
}

#[inline]
#[allow(non_snake_case)]
fn values(self) -> Self::Values {
let ($($tuple,)*) = self;
[$(
<$tuple as Into<Value>>::into($tuple)
<$tuple as Into<UntypedValue>>::into($tuple)
),*]
}

fn from_values<T>(mut values: T) -> Option<Self>
where
T: Iterator<Item = Value>,
{
let result = ($(
values.next().and_then(Value::try_into::<$tuple>)?,
)*);
if values.next().is_some() {
// Note: If the iterator yielded more items than
// necessary we create no value from this procedure
// as it is likely a bug.
return None
#[inline]
#[allow(non_snake_case)]
fn from_values(values: &[UntypedValue]) -> Option<Self> {
if let [$($tuple),*] = *values {
return Some(
( $( Into::into($tuple), )* )
)
}
Some(result)
None
}
}
};
Expand Down
4 changes: 4 additions & 0 deletions crates/wasmi/src/func/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ impl Func {
if expected_outputs.len() != outputs.len() {
return Err(FuncError::MismatchingResults { func: *self }).map_err(Into::into);
}
outputs
.iter_mut()
.zip(expected_outputs.iter().copied().map(Value::default))
.for_each(|(output, expected_output)| *output = expected_output);
// Note: Cloning an [`Engine`] is intentionally a cheap operation.
ctx.as_context().store.engine().clone().execute_func(
ctx.as_context_mut(),
Expand Down
Loading