-
Notifications
You must be signed in to change notification settings - Fork 80
Best practices for performance
We can get a lot of quick wins performance wise in our code-base by following the rules on https://springrts.com/wiki/Lua_Performance. Please refer to this document when reading the suggestions below
TEST 1 This is about localizing references. By default everything is reached from the global scope and often from a table. Example:
-- slow, avoid
math.min()
-- top of the file
local min = math.min
-- somewhere in your code
min(a, b)
TEST 4
This refers to the lua function math.max
. Accepts the highest value of 2. It's faster to manually check which is higher and assign that, compared to the function call. It doesn't matter for code that's ran everyone once in a while, but if it's performance critical code (hot path, often executed), it's wise to apply this optimization.
TEST 8 This test is to show that function definitions inside a loop, or scope, executed many times, costs a lot of overhead. Whenever possible, move your function definition someplace where defining it is only done once.
-- slow
local function test()
local testf = function () end
-- or even
for i = 1, 100 do
(function())
end
end
-- instead do
local testf = function () end
local function test()
for i = 1, 100 do
testf()
end
end
TEST 9 This is test is really important for sequential tables (so starting at 1 and always adding 1 to the index). When iterating over the table, calling pairs or ipairs is really slow compared to keeping track of the table size and accessing it +1 when iterating. This only works if the table is sequential!
-- slow
for j,v in ipairs(a) do
x=v
end
-- fast
for i=1,#a do
x=a[i]
end
TEST 11 This is a generic solution for pretty much anything. Whenever you have to access a certain key multiple times, reference it in a local. In case of tabels, it's always a reference, changing the reference, changes the original as well, it's not a copy.
local create_entity = game.surfaces.nauvis.create_entity
for i = 1, 100 do
create_entity(...)
end
-- and
local my_table = {
some_other_table = {
counter = 0,
}
}
-- slow
my_table.some_other_table.counter = my_table.some_other_table.counter + 1
my_table.some_other_table.counter = my_table.some_other_table.counter + 1
-- faster
local some_other_table = my_table.some_other_table
local counter = some_other_table.counter
counter = counter + 1
counter = counter + 1
some_other_table.counter = counter
TEST 12: table insert
When inserting items into a table, it's common to use table.insert
, but this is extremely slow. If you're in a loop, keep a counter and directly set table entries with it.
local current_counter = #my_table + 1
for i = 1,1000000 do
my_table[current_counter] = i
current_counter = current_counter + 1
end
If you want to append an item to a table without a loop, use: my_table[#my_table + 1]
TEST 12: table create When creating a table, lua does some smart memory allocating as it knows ahead of time what the size should be. It's wise to avoid using custom indexes, but it's also important to combine the creation of the array and setting the values. If you know ahead of time what the size will be and this is static, reserve some slots.
-- slow
local my_table = {}
my_table[1] = 'foo'
my_table[2] = 'bar'
my_table[3] = 'baz'
-- faster
local my_table = {true, true, true}
my_table[1] = 'foo'
my_table[2] = 'bar'
my_table[3] = 'baz'
-- fastest
local my_table = {'foo', 'bar', 'baz'}
TEST 13
Lua doesn't do well with static analyzation. Whenever it sees a table structure, it will create and insert that table structure. Meaning if you have a table definition inside a loop or function callback, it will create, initialize, and store this table in memory every single iteration. This also triggers heavy garbage collection. Note that this is only useful for static code, something that is not altered, like a color definition in factorio: {r = 255, g = 200, b = 100}
. A side-effect of this is that the table is never copied, it's always a reference, so make sure it's not modified.
-- slow
local my_table = {}
for i = 1, 100 do
my_table[i] = {r = 255, g = 200, b = 100} -- created and stored in memory, then garbage collected
end
-- fast
local my_table = {}
local color = {r = 255, g = 200, b = 100}
for i = 1, 100 do
my_table[i] = color
end
Having issues? Reach out on discord
Got a bug report, feature request or map idea? Open an issue
Looking to download maps? See releases or the public save directory
Interested in Localization? Check out the progress at Crowdin