Performance optimization: cache and reuse MultiValue
containers
This commit is contained in:
parent
a8017c10b9
commit
863d36d5a1
38
src/lua.rs
38
src/lua.rs
|
@ -90,6 +90,8 @@ struct ExtraData {
|
|||
|
||||
// Pool of preallocated `WrappedFailure` enums on the ref thread
|
||||
wrapped_failures_pool: Vec<c_int>,
|
||||
// Cache of recycled `MultiValue` containers
|
||||
multivalue_cache: Vec<MultiValue<'static>>,
|
||||
|
||||
// Index of `Option<Waker>` userdata on the ref thread
|
||||
#[cfg(feature = "async")]
|
||||
|
@ -167,6 +169,7 @@ pub(crate) static ASYNC_POLL_PENDING: u8 = 0;
|
|||
pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURES_POOL_SIZE: usize = 16;
|
||||
const MULTIVALUE_CACHE_SIZE: usize = 16;
|
||||
|
||||
/// Requires `feature = "send"`
|
||||
#[cfg(feature = "send")]
|
||||
|
@ -480,7 +483,8 @@ impl Lua {
|
|||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||
ref_stack_top,
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failures_pool: Vec::new(),
|
||||
wrapped_failures_pool: Vec::with_capacity(WRAPPED_FAILURES_POOL_SIZE),
|
||||
multivalue_cache: Vec::with_capacity(MULTIVALUE_CACHE_SIZE),
|
||||
#[cfg(feature = "async")]
|
||||
ref_waker_idx,
|
||||
hook_callback: None,
|
||||
|
@ -2031,7 +2035,7 @@ impl Lua {
|
|||
let lua = &mut (*upvalue).lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
let mut args = MultiValue::new_or_cached(lua);
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
|
@ -2099,7 +2103,7 @@ impl Lua {
|
|||
let lua = &mut (*upvalue).lua;
|
||||
lua.state = state;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
let mut args = MultiValue::new_or_cached(lua);
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
|
@ -2181,11 +2185,10 @@ impl Lua {
|
|||
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
|
||||
env.set(
|
||||
"unpack",
|
||||
self.create_function(|_, (tbl, len): (Table, Integer)| {
|
||||
Ok(MultiValue::from_vec(
|
||||
tbl.raw_sequence_values_by_len(Some(len))
|
||||
.collect::<Result<Vec<Value>>>()?,
|
||||
))
|
||||
self.create_function(|lua, (tbl, len): (Table, Integer)| {
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(tbl.raw_sequence_values_by_len(Some(len)))?;
|
||||
Ok(values)
|
||||
})?,
|
||||
)?;
|
||||
env.set("pending", {
|
||||
|
@ -2317,6 +2320,25 @@ impl Lua {
|
|||
pub(crate) unsafe fn hook_callback(&self) -> Option<HookCallback> {
|
||||
(*self.extra.get()).hook_callback.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_or_cached_multivalue(&self) -> MultiValue {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra.get();
|
||||
extra.multivalue_cache.pop().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn cache_multivalue(&self, mut multivalue: MultiValue) {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.multivalue_cache.len() < MULTIVALUE_CACHE_SIZE {
|
||||
multivalue.clear();
|
||||
extra.multivalue_cache.push(mem::transmute(multivalue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
|
|
31
src/multi.rs
31
src/multi.rs
|
@ -12,8 +12,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
|||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new();
|
||||
|
||||
let mut result = MultiValue::new_or_cached(lua);
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.to_lua(lua)?),
|
||||
Err(e) => {
|
||||
|
@ -21,14 +20,13 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
|
|||
result.push_front(Nil);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new();
|
||||
let mut v = MultiValue::new_or_cached(lua);
|
||||
v.push_front(self.to_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
|
@ -36,7 +34,9 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
|||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,30 +125,35 @@ impl<T> DerefMut for Variadic<T> {
|
|||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
self.0.into_iter().map(|e| e.to_lua(lua)).collect()
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
values
|
||||
.into_iter()
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic)
|
||||
.map(Variadic);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> ToLuaMulti<'lua> for () {
|
||||
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new())
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_cached(lua))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
lua.cache_multivalue(values);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
25
src/value.rs
25
src/value.rs
|
@ -162,6 +162,11 @@ impl<'lua> MultiValue<'lua> {
|
|||
pub fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_or_cached_multivalue()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Default for MultiValue<'lua> {
|
||||
|
@ -227,6 +232,11 @@ impl<'lua> MultiValue<'lua> {
|
|||
self.0.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
|
@ -234,7 +244,7 @@ impl<'lua> MultiValue<'lua> {
|
|||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -246,6 +256,19 @@ impl<'lua> MultiValue<'lua> {
|
|||
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
|
||||
self.0.drain(..).rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn refill(
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
|
||||
) -> Result<()> {
|
||||
self.0.clear();
|
||||
for value in iter {
|
||||
self.0.push(value?);
|
||||
}
|
||||
self.0.reverse();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
|
|
Loading…
Reference in a new issue