Performance optimization: cache and reuse MultiValue containers

This commit is contained in:
Alex Orlenko 2021-11-12 15:32:53 +00:00
parent a8017c10b9
commit 863d36d5a1
No known key found for this signature in database
GPG key ID: 4C150C250863B96D
3 changed files with 72 additions and 22 deletions

View file

@ -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.

View file

@ -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(())
}
}

View file

@ -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.