Lua memory usage of functions and function contents

If anyone is proficient in this area of the topic, please offer some insights.

Is it more memory efficient to store data inside a function and only call that function when needed to retrieve the data into the scope and let it garbage collect when the data is no longer in scope?

I have a lot of data for loot and items currently stored in tables and I’m wondering if I should move them into functions because the data isn’t created until the function is called? I’m not sure if that’s right.

debug.setmemorycategory("TableStyleMem");
local itemsTableMem = {
	{Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"};
	{Id="itemB"; Description="This is item b."; StackSize=31; Type="Food"};
	{Id="itemC"; Description="This is item c."; StackSize=30; Type="Tool"};
	{Id="itemD"; Description="This is item d."; StackSize=29; Type="Tool"};
	{Id="itemE"; Description="This is item e."; StackSize=28; Type="Resource"};
}

local retrieveItemA = itemsTableMem[1];


debug.setmemorycategory("FunctionStyleMem");
local itemFunctionMem = {
	function() return {Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"} end;
	function() return {Id="itemB"; Description="This is item b."; StackSize=31; Type="Food"} end;
	function() return {Id="itemC"; Description="This is item c."; StackSize=30; Type="Tool"} end;
	function() return {Id="itemD"; Description="This is item d."; StackSize=29; Type="Tool"} end;
	function() return {Id="itemE"; Description="This is item e."; StackSize=28; Type="Resource"} end;
}

local retrieveItemA = itemFunctionMem[1]();

According to memory categories, it shows a difference here… But I’m wondering where are the values within the function is stored and how much memory that takes up.

1 Like

When you have a variable in a function it is “dropped” when the function ends. This process is completely free. Garbage collection only happens to variables that are never dropped, which is almost always tables. A variable which points to a table (Even if it is anonymous like it is in your case, no name is ever given to it), is first dropped, then later the table it pointed to will be found with no references and thus garbage collected.
Whenever you create a table in lua by writing {}, it is always a new table. Your second style is not allowed to return the same table each time a function inside it is called, whereas the first style always returns the same table for a given index. So the first style involves fewer table creations and writes to the table, so it should be much more efficient.
In this example both ways are functionally the same, but only since you are using the table to store constants. If you needed to look something up each time, such as a market price for the item, you’d have to use the second method.

I understand that the first style is more computationally efficient, I’m more into trying to understand the amount of memory takes up since a function and its content surely takes up memory somewhere else. So is the second style more memory efficient or the first?

1 Like

The first style is likely to be more efficient, but not for the reasons you’re asking about. I say this because the first style only creates a table once per entry while the second one creates a table each time it is accessed. The second style also involves a function call, which is relatively expensive.

3 Likes

Further testing shows that table data appears to take up more memory highlighted in yellow. ITM is local variables referencing the already created data hance TableStyleMem = 0.009 MB while ITM = ~0 MB.

While the ones highlighted in green are data created during runtime. FunctionStyleMem storing reference to functions that will create the data when called, costing 0.001 MB. And when the function is called, it created new copies of the data IFM = 0.001 MB, taking up memory in the local scope.

Memory consumption shouldn’t be a large concern for lightweight purposes such as managing items/loot (unless you choose to have thousands of items in your game). The solution you proposed for lighter memory consumption leads to too much memory allocation work, which is expensive.

-- command bar test
local func = {function() return {Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"} end} local mem = {{Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"}} local st = tick() for _ = 1, 5000 do local t = func[1]() end print(tick() - st) st = tick() for _ = 1, 100 do local t = mem[1] end print(tick() - st)

-- values may differ depending on your system. function call overhead also
-- contributes to the slow performance of function-based item storage.
-- 0.0005795955657958984 -- function-based
-- 0.000013828277587890625 -- indexing-based

But then, these figures might not be that staggering depending on how you will use them.

Another downside is if you are comparing tables, Lua does not compare by their contents, it compares them by their internal hex ID.

-- command bar test
local func = {function() return {Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"} end} local mem = {{Id="itemA"; Description="This is item a."; StackSize=32; Type="Food"}} print(`Table in memory: {mem[1] == mem[1]}`) local ft1, ft2 = func[1](), func[1]() print(`Table created by function: {ft1 == ft2}`) print("ft1 =", ft1) print("ft2 =", ft2) print("ft1 hex =", tostring(ft1)) print("ft2 hex =", tostring(ft2)) print("We expect these tables to be the same content-wise, but Lua matches their hex IDs, not contents.")

Output:
-------------------------------------------------------------------
  06:36:49.217  Table in memory: true  -  Edit
  06:36:49.217  Table created by function: false  -  Edit
  06:36:49.218  ft1 =  ▼  {
                    ["Description"] = "This is item a.",
                    ["Id"] = "itemA",
                    ["StackSize"] = 32,
                    ["Type"] = "Food"
                 }  -  Edit
  06:36:49.218  ft2 =  ▼  {
                    ["Description"] = "This is item a.",
                    ["Id"] = "itemA",
                    ["StackSize"] = 32,
                    ["Type"] = "Food"
                 }  -  Edit
  06:36:49.219  ft1 hex = table: 0xffcbffab33541d7c  -  Edit
  06:36:49.220  ft2 hex = table: 0x955fdad1d9edb09c  -  Edit
  06:36:49.220  We expect these tables to be the same content-wise, but Lua matches their hex IDs, not contents.  -  Edit

Mind you that, according to the Creator Docs, each Roblox server has a maximum of 6.9 GB of memory, and you should start becoming concerned once your game consumes 3 GB (server-side), while you’re dealing with less than a MB.

It’s preferable to write conventional/clean code over micro-optimisation. You should always find better solutions before considering micro-optimisations.

I’m looking for ways to optimize memory on both client and server. I have likely thousands of tables containing data from items, loottables, cutscenes, dialogue scripts, item configurations, etc… Currently focused on client side optimizations first because usage on the server side is acceptable, but client side usage could be better.

I’m migrating data that are only situationally used such as cutscene dialogues, item configurations, etc to be stored in functions so they are only created when needed.

I just need more information on how much memory and how memory is calculated for functions that returns tables.

tl;dr;

-- E.g. How much memory does this function take up before ever being called?
local function getData()
    return {
        {Id="itemA"; Description="This is item a."; StackSize=1; Type="Food"};
        {Id="itemB"; Description="This is item b."; StackSize=2; Type="Food"};
        {Id="itemC"; Description="This is item c."; StackSize=3; Type="Food"};
        {Id="itemD"; Description="This is item d."; StackSize=4; Type="Food"};
        {Id="itemE"; Description="This is item e."; StackSize=5; Type="Food"};
    }
end

memory usage in Lua can vary depending on factors like how the engine is implemented.