From 93d36b90686c12a3755d8e64146b08ecaa789d59 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Mon, 6 Jun 2022 21:42:55 +0100 Subject: [PATCH] More performance optimization (userdata part) --- src/lua.rs | 118 ++++++++++++++++++++--------------------------- src/scope.rs | 9 ++-- src/serde/ser.rs | 15 ++++-- src/util.rs | 81 +++++++++++++++++++++++--------- 4 files changed, 124 insertions(+), 99 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index d00a40c..fd8bab7 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -538,7 +538,7 @@ impl Lua { #[cfg(feature = "async")] let ref_waker_idx = { mlua_expect!( - push_gc_userdata::>(ref_thread, None), + push_gc_userdata::>(ref_thread, None, true), "Error while creating Waker slot" ); ffi::lua_gettop(ref_thread) @@ -578,7 +578,7 @@ impl Lua { mlua_expect!( (|state| { - push_gc_userdata(state, Arc::clone(&extra))?; + push_gc_userdata(state, Arc::clone(&extra), true)?; protect_lua!(state, 1, 0, fn(state) { let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key); @@ -1408,29 +1408,15 @@ impl Lua { let _sg = StackGuard::new(self.state); check_stack(self.state, 3)?; - if self.unlikely_memory_error() { - let s = s.as_ref(); - ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len()); - } else { - push_string(self.state, s)?; - } + let protect = !self.unlikely_memory_error(); + push_string(self.state, s, protect)?; Ok(String(self.pop_ref())) } } /// Creates and returns a new empty table. pub fn create_table(&self) -> Result { - unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; - - if self.unlikely_memory_error() { - ffi::lua_newtable(self.state); - } else { - protect_lua!(self.state, 0, 1, fn(state) ffi::lua_newtable(state))?; - } - Ok(Table(self.pop_ref())) - } + self.create_table_with_capacity(0, 0) } /// Creates and returns a new empty table, with the specified capacity. @@ -1442,12 +1428,8 @@ impl Lua { let _sg = StackGuard::new(self.state); check_stack(self.state, 3)?; - if self.unlikely_memory_error() { - ffi::lua_createtable(self.state, narr, nrec); - } else { - push_table(self.state, narr, nrec)?; - } - + let protect = !self.unlikely_memory_error(); + push_table(self.state, narr, nrec, protect)?; Ok(Table(self.pop_ref())) } } @@ -1465,20 +1447,15 @@ impl Lua { let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; - - if self.unlikely_memory_error() { - ffi::lua_createtable(self.state, 0, lower_bound as c_int); - for (k, v) in iter { - self.push_value(k.to_lua(self)?)?; - self.push_value(v.to_lua(self)?)?; - ffi::lua_rawset(self.state, -3); - } - } else { - push_table(self.state, 0, lower_bound as c_int)?; - for (k, v) in iter { - self.push_value(k.to_lua(self)?)?; - self.push_value(v.to_lua(self)?)?; + let protect = !self.unlikely_memory_error(); + push_table(self.state, 0, lower_bound as c_int, protect)?; + for (k, v) in iter { + self.push_value(k.to_lua(self)?)?; + self.push_value(v.to_lua(self)?)?; + if protect { protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?; + } else { + ffi::lua_rawset(self.state, -3); } } @@ -1498,20 +1475,16 @@ impl Lua { let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; - - if self.unlikely_memory_error() { - ffi::lua_createtable(self.state, lower_bound as c_int, 0); - for (i, v) in iter.enumerate() { - self.push_value(v.to_lua(self)?)?; - ffi::lua_rawseti(self.state, -2, (i + 1) as Integer); - } - } else { - push_table(self.state, lower_bound as c_int, 0)?; - for (i, v) in iter.enumerate() { - self.push_value(v.to_lua(self)?)?; + let protect = !self.unlikely_memory_error(); + push_table(self.state, lower_bound as c_int, 0, protect)?; + for (i, v) in iter.enumerate() { + self.push_value(v.to_lua(self)?)?; + if protect { protect_lua!(self.state, 2, 1, |state| { ffi::lua_rawseti(state, -2, (i + 1) as Integer); })?; + } else { + ffi::lua_rawseti(self.state, -2, (i + 1) as Integer); } } @@ -1972,7 +1945,8 @@ impl Lua { let _sg = StackGuard::new(self.state); check_stack(self.state, 3)?; - push_string(self.state, name)?; + let protect = !self.unlikely_memory_error(); + push_string(self.state, name, protect)?; ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); self.pop_value() @@ -2260,7 +2234,8 @@ impl Lua { } Value::Error(err) => { - push_gc_userdata(self.state, WrappedFailure::Error(err))?; + let protect = !self.unlikely_memory_error(); + push_gc_userdata(self.state, WrappedFailure::Error(err), protect)?; } } @@ -2445,7 +2420,7 @@ impl Lua { let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len(); #[cfg(feature = "async")] let metatable_nrec = metatable_nrec + methods.async_meta_methods.len(); - push_table(self.state, 0, metatable_nrec as c_int)?; + push_table(self.state, 0, metatable_nrec as c_int, true)?; for (k, m) in methods.meta_methods { self.push_value(Value::Function(self.create_callback(m)?))?; rawset_field(self.state, -2, k.validate()?.name())?; @@ -2466,7 +2441,7 @@ impl Lua { let mut field_getters_index = None; let field_getters_nrec = fields.field_getters.len(); if field_getters_nrec > 0 { - push_table(self.state, 0, field_getters_nrec as c_int)?; + push_table(self.state, 0, field_getters_nrec as c_int, true)?; for (k, m) in fields.field_getters { self.push_value(Value::Function(self.create_callback(m)?))?; rawset_field(self.state, -2, &k)?; @@ -2478,7 +2453,7 @@ impl Lua { let mut field_setters_index = None; let field_setters_nrec = fields.field_setters.len(); if field_setters_nrec > 0 { - push_table(self.state, 0, field_setters_nrec as c_int)?; + push_table(self.state, 0, field_setters_nrec as c_int, true)?; for (k, m) in fields.field_setters { self.push_value(Value::Function(self.create_callback(m)?))?; rawset_field(self.state, -2, &k)?; @@ -2492,7 +2467,7 @@ impl Lua { #[cfg(feature = "async")] let methods_nrec = methods_nrec + methods.async_methods.len(); if methods_nrec > 0 { - push_table(self.state, 0, methods_nrec as c_int)?; + push_table(self.state, 0, methods_nrec as c_int, true)?; for (k, m) in methods.methods { self.push_value(Value::Function(self.create_callback(m)?))?; rawset_field(self.state, -2, &k)?; @@ -2623,13 +2598,14 @@ impl Lua { let func = mem::transmute(func); let extra = Arc::clone(&self.extra); - push_gc_userdata(self.state, CallbackUpvalue { data: func, extra })?; - if self.unlikely_memory_error() { - ffi::lua_pushcclosure(self.state, call_callback, 1); - } else { + let protect = !self.unlikely_memory_error(); + push_gc_userdata(self.state, CallbackUpvalue { data: func, extra }, protect)?; + if protect { protect_lua!(self.state, 1, 1, fn(state) { ffi::lua_pushcclosure(state, call_callback, 1); })?; + } else { + ffi::lua_pushcclosure(self.state, call_callback, 1); } Ok(Function(self.pop_ref())) @@ -2686,13 +2662,14 @@ impl Lua { let func = &*(*upvalue).data; let fut = func(lua, args); let extra = Arc::clone(&(*upvalue).extra); - push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra })?; - if lua.unlikely_memory_error() { - ffi::lua_pushcclosure(state, poll_future, 1); - } else { + let protect = !lua.unlikely_memory_error(); + push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?; + if protect { protect_lua!(state, 1, 1, fn(state) { ffi::lua_pushcclosure(state, poll_future, 1); })?; + } else { + ffi::lua_pushcclosure(state, poll_future, 1); } Ok(1) @@ -2752,13 +2729,15 @@ impl Lua { let func = mem::transmute(func); let extra = Arc::clone(&self.extra); - push_gc_userdata(self.state, AsyncCallbackUpvalue { data: func, extra })?; - if self.unlikely_memory_error() { - ffi::lua_pushcclosure(self.state, call_callback, 1); - } else { + let protect = !self.unlikely_memory_error(); + let upvalue = AsyncCallbackUpvalue { data: func, extra }; + push_gc_userdata(self.state, upvalue, protect)?; + if protect { protect_lua!(self.state, 1, 1, fn(state) { ffi::lua_pushcclosure(state, call_callback, 1); })?; + } else { + ffi::lua_pushcclosure(self.state, call_callback, 1); } Function(self.pop_ref()) @@ -2833,10 +2812,11 @@ impl Lua { // We push metatable first to ensure having correct metatable with `__gc` method ffi::lua_pushnil(self.state); self.push_userdata_metatable::()?; + let protect = !self.unlikely_memory_error(); #[cfg(not(feature = "lua54"))] - push_userdata(self.state, data)?; + push_userdata(self.state, data, protect)?; #[cfg(feature = "lua54")] - push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int)?; + push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int, protect)?; ffi::lua_replace(self.state, -3); ffi::lua_setmetatable(self.state, -2); diff --git a/src/scope.rs b/src/scope.rs index 8fd9d90..ab962b3 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -355,13 +355,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { crate::util::push_userdata::>>>( lua.state, UserDataCell::new(data.clone()), + true, )?; ffi::lua_touserdata(lua.state, -1) }; // Prepare metatable, add meta methods first and then meta fields let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1; - push_table(lua.state, 0, meta_methods_nrec as c_int)?; + push_table(lua.state, 0, meta_methods_nrec as c_int, true)?; for (k, m) in ud_methods.meta_methods { let data = data.clone(); @@ -377,7 +378,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let mut field_getters_index = None; let field_getters_nrec = ud_fields.field_getters.len(); if field_getters_nrec > 0 { - push_table(lua.state, 0, field_getters_nrec as c_int)?; + push_table(lua.state, 0, field_getters_nrec as c_int, true)?; for (k, m) in ud_fields.field_getters { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; @@ -389,7 +390,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let mut field_setters_index = None; let field_setters_nrec = ud_fields.field_setters.len(); if field_setters_nrec > 0 { - push_table(lua.state, 0, field_setters_nrec as c_int)?; + push_table(lua.state, 0, field_setters_nrec as c_int, true)?; for (k, m) in ud_fields.field_setters { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; @@ -402,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let methods_nrec = ud_methods.methods.len(); if methods_nrec > 0 { // Create table used for methods lookup - push_table(lua.state, 0, methods_nrec as c_int)?; + push_table(lua.state, 0, methods_nrec as c_int, true)?; for (k, m) in ud_methods.methods { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; diff --git a/src/serde/ser.rs b/src/serde/ser.rs index cb2141e..7df27cd 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -327,10 +327,17 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { lua.push_ref(&self.table.0); lua.push_value(value)?; - protect_lua!(lua.state, 2, 0, fn(state) { - let len = ffi::lua_rawlen(state, -2) as Integer; - ffi::lua_rawseti(state, -2, len + 1); - }) + if lua.unlikely_memory_error() { + let len = ffi::lua_rawlen(lua.state, -2) as Integer; + ffi::lua_rawseti(lua.state, -2, len + 1); + ffi::lua_pop(lua.state, 1); + Ok(()) + } else { + protect_lua!(lua.state, 2, 0, fn(state) { + let len = ffi::lua_rawlen(state, -2) as Integer; + ffi::lua_rawseti(state, -2, len + 1); + }) + } } } diff --git a/src/util.rs b/src/util.rs index 063a5c6..aca50f1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -251,17 +251,33 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { pub unsafe fn push_string + ?Sized>( state: *mut ffi::lua_State, s: &S, + protect: bool, ) -> Result<()> { let s = s.as_ref(); - protect_lua!(state, 0, 1, |state| { + if protect { + protect_lua!(state, 0, 1, |state| { + ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); + }) + } else { ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); - }) + Ok(()) + } } // Uses 3 stack spaces, does not call checkstack. #[inline] -pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> { - protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec)) +pub unsafe fn push_table( + state: *mut ffi::lua_State, + narr: c_int, + nrec: c_int, + protect: bool, +) -> Result<()> { + if protect { + protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec)) + } else { + ffi::lua_createtable(state, narr, nrec); + Ok(()) + } } // Uses 4 stack spaces, does not call checkstack. @@ -281,10 +297,14 @@ where // Internally uses 3 stack spaces, does not call checkstack. #[cfg(not(feature = "luau"))] #[inline] -pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { - let ud = protect_lua!(state, 0, 1, |state| { +pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> { + let ud = if protect { + protect_lua!(state, 0, 1, |state| { + ffi::lua_newuserdata(state, mem::size_of::()) as *mut T + })? + } else { ffi::lua_newuserdata(state, mem::size_of::()) as *mut T - })?; + }; ptr::write(ud, t); Ok(()) } @@ -292,7 +312,7 @@ pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { // Internally uses 3 stack spaces, does not call checkstack. #[cfg(feature = "luau")] #[inline] -pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { +pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> { unsafe extern "C" fn destructor(ud: *mut c_void) { let ud = ud as *mut T; if *(ud.offset(1) as *mut u8) == 0 { @@ -300,10 +320,14 @@ pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { } } - let ud = protect_lua!(state, 0, 1, |state| { - let size = mem::size_of::() + 1; + let size = mem::size_of::() + 1; + let ud = if protect { + protect_lua!(state, 0, 1, |state| { + ffi::lua_newuserdatadtor(state, size, destructor::) as *mut T + })? + } else { ffi::lua_newuserdatadtor(state, size, destructor::) as *mut T - })?; + }; ptr::write(ud, t); *(ud.offset(1) as *mut u8) = 0; // Mark as not destructed @@ -313,10 +337,19 @@ pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { // Internally uses 3 stack spaces, does not call checkstack. #[cfg(feature = "lua54")] #[inline] -pub unsafe fn push_userdata_uv(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> { - let ud = protect_lua!(state, 0, 1, |state| { +pub unsafe fn push_userdata_uv( + state: *mut ffi::lua_State, + t: T, + nuvalue: c_int, + protect: bool, +) -> Result<()> { + let ud = if protect { + protect_lua!(state, 0, 1, |state| { + ffi::lua_newuserdatauv(state, mem::size_of::(), nuvalue) as *mut T + })? + } else { ffi::lua_newuserdatauv(state, mem::size_of::(), nuvalue) as *mut T - })?; + }; ptr::write(ud, t); Ok(()) } @@ -349,8 +382,12 @@ pub unsafe fn take_userdata(state: *mut ffi::lua_State) -> T { // Pushes the userdata and attaches a metatable with __gc method. // Internally uses 3 stack spaces, does not call checkstack. -pub unsafe fn push_gc_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { - push_userdata(state, t)?; +pub unsafe fn push_gc_userdata( + state: *mut ffi::lua_State, + t: T, + protect: bool, +) -> Result<()> { + push_userdata(state, t, protect)?; get_gc_metatable::(state); ffi::lua_setmetatable(state, -2); Ok(()) @@ -505,7 +542,7 @@ pub unsafe fn init_userdata_metatable( // Push `__index` generator function init_userdata_metatable_index(state)?; - push_string(state, "__index")?; + push_string(state, "__index", true)?; let index_type = ffi::lua_rawget(state, -3); match index_type { ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => { @@ -530,7 +567,7 @@ pub unsafe fn init_userdata_metatable( // Push `__newindex` generator function init_userdata_metatable_newindex(state)?; - push_string(state, "__newindex")?; + push_string(state, "__newindex", true)?; let newindex_type = ffi::lua_rawget(state, -3); match newindex_type { ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => { @@ -758,7 +795,7 @@ pub unsafe fn init_gc_metatable( ) -> Result<()> { check_stack(state, 6)?; - push_table(state, 0, 3)?; + push_table(state, 0, 3, true)?; #[cfg(not(feature = "luau"))] { @@ -836,7 +873,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { } }?; - push_string(state, &*err_buf)?; + push_string(state, &*err_buf, true)?; (*err_buf).clear(); Ok(1) @@ -857,7 +894,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { callback_error(state, |_| Err(Error::CallbackDestructed)) } - push_table(state, 0, 26)?; + push_table(state, 0, 26, true)?; ffi::lua_pushcfunction(state, destructed_error); for &method in &[ "__add", @@ -914,7 +951,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { // Create error print buffer init_gc_metatable::(state, None)?; - push_gc_userdata(state, String::new())?; + push_gc_userdata(state, String::new(), true)?; protect_lua!(state, 1, 0, fn(state) { let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);