Optimizing Multidimensional Arrays With table.create

Hello Roblox developers! Do you work with arrays? Chances are you probably do if you’re reading this so here is some useful information about the allocation arrays in Roblox!

It might seem like table.create is not a useful function at first glance but it can give some nice performance benefits when working with large arrays. Arrays in Roblox are stored in memory. These arrays are initially created with little to no extra memory for values. Adding a new value to this array means that memory must be immediately allocated for the new value. That can be expensive especially with multidimensional arrays. table.create allows you to preallocate a certain amount of space for an array in one go meaning Roblox doesn’t spend as much time allocating the values you add!

Here are some examples:

-- This code allocates and populates a 2D array!
local height = 50
local width = 100
local array = table.create(width)

for x=1, width do
	array[x] = table.create(height) -- Create a column
	for y=1, height do
		array[x][y] = x * y -- Populate it!
	end
end
-- You can do any dimensional array! 3D, 4D, even 100D if you're willing to type out the code or use a helper function.
local height = 50
local width = 100
local array = table.create(width)

for x=1, width do
	array[x] = table.create(height) -- Create a column
	for y=1, height do
		array[x][y] = table.create(depth) -- Create a layer
		for z=1, depth do
			array[x][y][z] = x * y * z -- Populate it!
		end
	end
end

You should try to use table.create if you know the initial size of your array. It makes sure that just enough space is allocated for you. That means there’s much less of a performance cost on large multidimensional arrays because Roblox doesn’t need to allocate memory every time you add a new value.

Finally, don’t be “greedy.” Allocating arrays you don’t know the initial length of can waste memory! For example, this is bad! table.create(1000) will create an array with space for 1000 values. If it takes a while to fill this array this memory could be sitting there unused for a long time! It’s important to make sure you don’t use too much memory on the server or client because once you run out the game or server your code is running on will crash.

More information is explained by zeuxcg in the replies here: Luau Recap: November 2019
You may find answers there! :smile:

33 Likes

Do you know if there’s a way to mark a table as being finished with? This means that if we overshot our create estimate, we could tell Roblox to flush away those empty fields, freeing up memory.

I’m not sure about this. My guess is that using table.remove you will be able to deallocate one value at a time. I’d assume that writing a value (or specifying a non-nil value) and immediately setting it back to nil would also trigger deallocation.

Maybe @zeuxcg can answer this?

1 Like

Creating the array with the initial size also avoids re allocations.

100d array for fun

local function Recurse(tbl,CurrentAmount,...)
	if CurrentAmount and ... then
		for i=1,CurrentAmount do
			local NewTbl = table.create((...))
			Recurse(NewTbl,...)
			tbl[i] = NewTbl
		end
	end
end
local function MultiDimensionalArray(...)
	local tbl = table.create((...))
	Recurse(tbl,...)
	return tbl
end
local tbl = MultiDimensionalArray(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
tbl[1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1] = 1
print(tbl[1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1][1])
4 Likes

You can also set the default value for each index in the table:

-- Allocate size of 100 w/ each value being "abc"
local t = table.create(100, "abc")
print(t[15] == "abc")
5 Likes

There’s no way to shrink a table to fit programmatically. It is sometimes done automatically if enough elements are removed.

4 Likes

Would table.freeze shrink tables in this way?
(Sorry for the bump)