The first rule of optimisation is “Don’t”.
But we at CC2maps.com aren’t like normal people, so we’ve done it anyway.
The second rule (in my head) is “measure”. Followed by “don’t do stuff you don’t need to do – eg, repeating things”

Hot on the heels of my earlier post More optimization! I had a thought.
Quite a lot of what goes on in the vehicle control screen involves iterating through all the vehicles on the map, deciding to display them or not, calculating distances to other units etc.
There is a fairly common pattern repeated in the lua code in a lot of scripts:
local vehicle_count = update_get_map_vehicle_count()
for i = 0, vehicle_count - 1, 1 do
local vehicle = update_get_map_vehicle_by_index(i)
if vehicle:get() then
-- do something with vehicle
end
end
This is actually pretty fast already, but it still is done several times. I think even unmodded this is called 3-4 times per update(). With revolution this grows to 5 and occasionally 7 times.
All this calling into native code to get the vehicle objects, which I suspect gets a fresh object every time. We could save time here if we didn’t do this over and over.
My earlier attempt at memoization met with mixed results, partly because I was using random access to a table to check if my values were cached, table indexing is quite expensive (due to computing hashes). But I don’t need that here, I just need to get the whole table of vehicle objects once per frame rather than 4-6 times per frame.
So I now have this:
g_vt = {}
g_vt_tick = 0
function get_vehicles_table()
local now = update_get_logic_tick()
if now > g_vt_tick then
g_vt = nil
end
if g_vt == nil then
g_vt_tick = now
g_vt = {}
local vt = g_vt
local vehicle_count = update_get_map_vehicle_count()
for i = 0, vehicle_count - 1, 1 do
local vehicle = update_get_map_vehicle_by_index(i)
if vehicle:get() then
table.insert(vt, vehicle)
end
end
end
return g_vt;
end
Now, If I just want to iterate all the vehicles, I can do:
for x, vehicle in pairs(get_vehicles_table()) do
local vehicle_team = vehicle:get_team()
local vehicle_attached_parent_id = vehicle:get_attached_parent_id()
<etc>
The first time I call get_vehicles_table() this tick, it creates a fresh table, and then we just iterate it using pairs, we don’t care about the order so this is very fast.
Lets see the timings!
timer armed
------- >
start timer 3 1748102164 8586
done timer 1748102184
calls 9845
calls/sec 492.25
timer armed
------- >
start timer 3 1748102203 9137
done timer 1748102223
calls 9694
calls/sec 484.7
Woo! 484+ calls/sec! At the end of my previous post, without this change we were at about 280 calls/sec.
So this is significant!!!
Also included in this is using table.insert(fow_visible, bool) to store the island fog of war data rather than table[id] = bool. That bought me another 15 calls/sec.
Lua direct table access (eg, associative array access) seems a lot slower than simply iterating the whole thing. I guess there probably comes a point where indexer access is faster than iteration where there is a lot of data.
We shall see!



One response to “Third rule of Optimisation”
[…] my previous post I got a significant speedup by using my new get_vehicles_table() function. Returning only one copy […]
LikeLike