From 4d4a1452757026c0fe68c3a63825c7dcc82e1f52 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Thu, 26 Feb 2026 20:17:07 -0500 Subject: [PATCH] perf: O(1) upvalue access by storing upvalues as a tuple Convert the upvalue list stored in lua_closure tuples to an Erlang tuple, enabling O(1) indexed access via elem/2 instead of O(N) linear scans via Enum.at/2. Changes: - executor.ex :closure handler: wrap captured_upvalues with List.to_tuple/1 before building the closure - executor.ex get_upvalue/set_upvalue: Enum.at -> elem - executor.ex :parent_upvalue case: Enum.at -> elem - stdlib.ex lua_load: empty upvalues %{} -> {} - test/support/lua_test_case.ex dofile helper: empty upvalues [] -> {} Co-Authored-By: Claude Sonnet 4.6 --- lib/lua/vm/executor.ex | 8 ++++---- lib/lua/vm/stdlib.ex | 2 +- test/support/lua_test_case.ex | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/lua/vm/executor.ex b/lib/lua/vm/executor.ex index e31e4e6..da2184d 100644 --- a/lib/lua/vm/executor.ex +++ b/lib/lua/vm/executor.ex @@ -161,7 +161,7 @@ defmodule Lua.VM.Executor do # get_upvalue defp do_execute([{:get_upvalue, dest, index} | rest], regs, upvalues, proto, state) do - cell_ref = Enum.at(upvalues, index) + cell_ref = elem(upvalues, index) value = Map.get(state.upvalue_cells, cell_ref) regs = put_elem(regs, dest, value) do_execute(rest, regs, upvalues, proto, state) @@ -169,7 +169,7 @@ defmodule Lua.VM.Executor do # set_upvalue defp do_execute([{:set_upvalue, index, source} | rest], regs, upvalues, proto, state) do - cell_ref = Enum.at(upvalues, index) + cell_ref = elem(upvalues, index) value = elem(regs, source) state = %{state | upvalue_cells: Map.put(state.upvalue_cells, cell_ref, value)} do_execute(rest, regs, upvalues, proto, state) @@ -438,11 +438,11 @@ defmodule Lua.VM.Executor do {:parent_upvalue, index, _name}, {cells, state} -> # Share the parent's upvalue cell - {[Enum.at(upvalues, index) | cells], state} + {[elem(upvalues, index) | cells], state} end) captured_upvalues = Enum.reverse(captured_upvalues_reversed) - closure = {:lua_closure, nested_proto, captured_upvalues} + closure = {:lua_closure, nested_proto, List.to_tuple(captured_upvalues)} regs = put_elem(regs, dest, closure) do_execute(rest, regs, upvalues, proto, state) end diff --git a/lib/lua/vm/stdlib.ex b/lib/lua/vm/stdlib.ex index 12ffdce..1df70d9 100644 --- a/lib/lua/vm/stdlib.ex +++ b/lib/lua/vm/stdlib.ex @@ -398,7 +398,7 @@ defmodule Lua.VM.Stdlib do # Compiler currently never returns errors, always succeeds {:ok, prototype} = Lua.Compiler.compile(ast) # Create a closure from the compiled prototype - closure = {:lua_closure, prototype, %{}} + closure = {:lua_closure, prototype, {}} {[closure], state} {:error, reason} -> diff --git a/test/support/lua_test_case.ex b/test/support/lua_test_case.ex index da195f6..624d7fe 100644 --- a/test/support/lua_test_case.ex +++ b/test/support/lua_test_case.ex @@ -81,7 +81,7 @@ defmodule Lua.TestCase do case Lua.Compiler.compile(ast, source: chunk_name) do {:ok, proto} -> # Return a closure that will execute the proto when called - closure = {:lua_closure, proto, []} + closure = {:lua_closure, proto, {}} {[closure], state} {:error, error} ->