Skip to content

perf: read Lua values straight off the stack - #720

Merged
makspll merged 3 commits into
mainfrom
agent/lua-stack-conversion
Nov 14, 2026
Merged

makspll merged 3 commits into
mainfrom
agent/lua-stack-conversion

Conversation

@makspll

@makspll makspll commented Nov 13, 2026 •

Copy link
Copy Markdown
Owner

Lua values are read straight off the stack: LuaScriptValue and MultiLuaScriptValue implement mlua's from_stack hooks, so strings are read in place and tables are walked with the raw API (lua_next, lua_rawiter on Luau) without creating a Lua reference per string or table. Functions and userdata convert as before. After #717, half of a table's conversion was mlua creating those references.

lua_next raises an error when the table changed under it, which a __gc finalizer can do: converting an entry may allocate (a number key turned into a string) and so run a collection. Tables are therefore iterated inside a protected call, one per table (as Table::for_each does), so the error becomes a conversion error instead of jumping over the Rust frames of the conversion:

  • Lua 5.2+: a plain lua_pcall of iterate_table, pushing a C function without upvalues allocates nothing there.
  • Lua 5.1 / LuaJIT: pushing the function allocates and can itself raise, so mlua's Lua::exec_raw makes the call.
  • Luau needs none: lua_rawiter raises no errors.

Same results on every variant: a test converts the same values both ways, and a table changed by a finalizer during its conversion is an error, on lua54, luau, lua51 and luajit.

Prebuilt binaries, 3 alternating rounds, two tables per call:

bench main this PR with exec_raw on every variant unprotected
function/call table args Lua 2.10 µs 1.38 µs (−34%) 1.59 µs 1.43 µs
function/call 4 args Lua 1.26 µs 0.96 µs (−24%) 1.09 µs 0.93 µs

LuaScriptValue and MultiLuaScriptValue implement mlua's from_stack hooks:
strings are read in place and tables are walked with the raw iteration API
(lua_next, or lua_rawiter on Luau), so converting them creates no Lua
references. Functions and userdata convert as before, results are the same.

Adds a test checking both conversions agree, on every Lua variant.
@semanticdiff-com

semanticdiff-com Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs  23% smaller

lua_next raises an error when the table changed under it, which a finalizer
run by a collection during the conversion can do. The error jumped over the
Rust frames of the conversion.
@makspll

makspll commented Nov 14, 2026

Copy link
Copy Markdown
Owner Author

/bench

@makspll makspll added the performance Things to do with making the crate faster label Nov 14, 2026
@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

ProjectBevy Scripting
Branchagent/lua-stack-conversion
Testbedlinux-gha

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🐰 View full continuous benchmarking report in Bencher

@makspll
makspll merged commit c2d198e into main Nov 14, 2026
19 checks passed
@makspll
makspll deleted the agent/lua-stack-conversion branch November 14, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Things to do with making the crate faster

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant