Performance tips for LUA

Place to post guides, observations, things related to modding that are not mods themselves.
d3x0r
Filter Inserter
Filter Inserter
Posts: 316
Joined: Sun Jun 04, 2017 8:56 am
Contact:

Performance tips for LUA

Post by d3x0r »

Thread: Performance-wise questions / good practices

Investigated some common things; especially where performance is needed.

In order to check, it is useful to use the value in the loop; for instance, for in pairs() gives you the resolved thing from the table, whereas you have to do table[a] to get the value using a numeric index.

for the following timings, the table is 1000 entries, 100 loops of each are done to get 100,000

slowest (4.9ms for 100,000)

Code: Select all

for a,b in pairs( c ) do j=b end 


medium ( 3.4ms for 1000)

Code: Select all

for a=1, #c do j=table[a] end
for a=1,N do j=table[a] end 


fastest (2.2ms)

Code: Select all

while( current ) do current = curent.next end 



Another thing to note, the larger the array, say 10,000 entries instead of 1000 entries it is slower to access the last 1000 than the first 1000

(3.4ms)

Code: Select all

 for a=1, 1000 do j = table[a] end 

(5.3ms)

Code: Select all

 for a=9001, 10000 do j = table[a] end 


A function call is really slow. Passing more parameters to a function call slows it down more; actually 1 or 2 parameters isn't that much slower 3+ parameters has a significant hit

(4.0ms)

Code: Select all

 for a=1, 100000 do  callFunction() end 

(4.3ms)

Code: Select all

 for a=1, 100000 do  callFunction(1) end 

(4.3ms)

Code: Select all

 for a=1, 100000 do  callFunction(1,2) end 

(6.1ms)

Code: Select all

 for a=1, 100000 do  callFunction(1,2,3) end 

(6.5ms)

Code: Select all

 for a=1, 100000 do  callFunction(1,2,3,4) end 

(6.7ms)

Code: Select all

 for a=1, 100000 do  callFunction(1,2,3,4,5) end 



Full Code - 10 loops of 10,000 entries; was previously 100 loops of 1000 entries.
The linked list is constant either way.

Code: Select all

function call1( a, b, c,d )
end
local oneThousandList;
local oneThousand;

script.on_event(defines.events.on_tick, function(event)
	if not oneThousand then
		oneThousand = {};
		-- init an array
		for i=1, 10000 do
			oneThousand[i] = 1;
		end
		local next;
		-- build linked list
		for i=1, 10000 do
			oneThousandList = { next=next };
			next = oneThousandList;
		end
	end
	if true then
		for k=1, 10 do  -- 0.5
			for i=1, #oneThousand do
				--call1();  -- 4.0
				--call1( 1);  -- 4.3
				--call1( 1,2);  -- 4.3
				--call1( 1,3,4 );  -- 6.1
				--call1( 1,2,3,4);  -- 6.3
				call1( 1,2,3,4,5);  -- 6.6
			end
		end
	end

	if false then
		for k=1, 10 do    -- 0.48
			local cur = oneThousandList;
			while cur do  -- 2.2    64.7%   44%
				cur = cur.next;  
			end
		end
	end
	if false then
		for k=1, 10 do    -- 0.48
			for i=1, 10000 do
				j = oneThousand[i];  -- 3.4  -- 69%
					  -- (3.4-1000 of 10000)  ( 5.3  9001-10000)
			end
		end
	end

	if false then
		for k=1, 10 do    -- 3.4 (without j=b)
			for a,b in pairs( oneThousand ) do 
				j = b;  -- 4.9 - 5.0
			end
		end
	end
end)
User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5341
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: Performance tips for LUA

Post by Klonan »

In general, with what mods do, Lua is not the slow part of the script, its the API calls that take the majority of the script time,

So doing your best to cache API values and minimize excessive calls is good,

example bad:

Code: Select all

for i = 1, game.surfaces[1].count_entities_filtered{name = "stone-wall"} do
  game.surfaces[1].find_entities_filtered{name = "stone-wall"}[i].health = 1000
end
Example good:

Code: Select all

local entities = game.surfaces[1].find_entities_filtered{name = "stone-wall"}
for i = 1, #entities do
  entities[i].health = 1000
end
Also doing excessively large operations during on_tick is bad, something like updating a gui every tick, in general you'd only need to update some Gui if it changes, if you can write it that way, if not then using a simple return can help prevent a lot of wasted work:

Code: Select all

if (game.tick % 60) ~= 0 then return end
Post Reply

Return to “Modding discussion”