Nice work. I originally made this mod as a test of the reactor model, and released it after it seemed to work, I never really tried too hard to optimise it.Vegemeister wrote:I'm playing Factorio on integrated graphics, and the game is already memory-bandwidth-bound to start with, so I have to worry about performance more than many of you. As my atomic-powered factory grew, it started dipping below 60 UPS (well, 59.9) at times. At first I was able to stave off the problem by switching my power plant to a combinator-free, pump-free design, and instead keeping a close eye on the power graph to make sure demand was comfortably between 20-100% of capacity.
At 50 reactors, that was no longer good enough. I turned on show_time_used_percent in the debug menu, and found that mod-Reactors itself was fluctuating between 3 and 4 milliseconds of execution time per update. Switching to solar would eliminate the problem, but that's not nearly as cool as atomic power. So I started tinkering.
I wasn't able to find anything online about using a profiler on a loaded Factorio mod, so I wrapped Factorio's --benchmark command line option in a bash script, and threw together some very ugly python to measure the performance across git commits.
I was able to considerably reduce the CPU usage of this mod, from ~3 ms/update to ~0.5 ms/update, as measured by the debug info with the GUI on. Changes were as follows:
The benchmarks didn't have super-good test-retest consistency (always improvement, though), but here's a fairly representative run. The difference isn't as big as with the GUI on, because the headless benchmark means the simulation isn't competing with the iGPU for memory bandwidth:
- Time between full reactor updates increased from 5 ticks to 30 ticks.
- Reactor thermal mass increased and power increment/decrement decreased by a factor of 6, to preserve the control dynamics.
- Partial updates (which set the coolant temperature) run once every 5 ticks instead of on every tick.
- Coolant tank volume increased from 500 units to 2000 units, in order to counteract temperature sag from not running partial updates as frequently.
I don't know much about Factorio mod packaging/versioning and I don't want to step on anyone's toes, so I'm not gonna post a zip. But here's a patch:Code: Select all
Commit avg ms min ms max ms Message 0076654 5.21 4.36 15.52 Update fluid temperature less often. ef8d545 5.98 4.75 15.97 Refactor looping constructs. 5db3dd3 5.97 4.70 16.16 Update reactors less often. 7c81f64 6.66 5.42 16.50 Unix line endings. 7048c62 6.62 5.36 16.45 Initial commit
Code: Select all
diff --git a/control.lua b/control.lua index b20b17e..04b7df4 100644 --- a/control.lua +++ b/control.lua @@ -14,7 +14,8 @@ SIGNAL_REACTOR_TARGET = {type="virtual", name="signal-reactor-target"} SIGNAL_REACTOR_STOP = {type="virtual", name="signal-reactor-stop"} SIGNAL_REACTOR_STOPPING = {type="virtual", name="signal-reactor-stopping"} -TICKS_PER_UPDATE = 5 -- each reactor and cooling tower gets updated once every 5 ticks +TICKS_PER_UPDATE = 30 -- each reactor and cooling tower gets updated once every 30 ticks +TICKS_PER_QUICK_UPDATE = 5 -- coolant temperature gets set once every 5 ticks UPDATES_PER_SECOND = 60 / TICKS_PER_UPDATE MAX_POWER = 26000 -- kilowatts @@ -23,8 +24,8 @@ MIN_POWER = ["fission-reaction"] = MAX_POWER * 0.2, ["breeder-reaction"] = MAX_POWER * 0.2 } -POWER_INCREMENT = MAX_POWER / UPDATES_PER_SECOND / 10 -- 10 seconds to max from zero -POWER_DECREMENT = MAX_POWER / UPDATES_PER_SECOND / 10 -- 10 seconds to zero from max +POWER_INCREMENT = MAX_POWER / UPDATES_PER_SECOND / 60 -- 60 seconds to max from zero +POWER_DECREMENT = MAX_POWER / UPDATES_PER_SECOND / 60 -- 60 seconds to zero from max CRAFTING_INCREMENT = { ["fission-reaction"] = 1 / 50 / UPDATES_PER_SECOND / MIN_POWER["fission-reaction"], @@ -33,7 +34,7 @@ CRAFTING_INCREMENT = AMBIENT_TEMP = 15 OPERATING_TEMP = 150 SCRAM_TEMP = 200 -- overheat trigger temp -REACTOR_MASS = 2000 +REACTOR_MASS = 12000 function init_global() global = global or {} @@ -304,6 +305,9 @@ end function update_reactor(index) local reactor = global.reactors[index] + if not reactor then + return + end -- update core if reactor.scram then @@ -413,20 +417,15 @@ function update_tower(index) end script.on_event(defines.events.on_tick, function(event) - for i,_ in pairs(global.reactors) do + local quick_offset = (event.tick % TICKS_PER_QUICK_UPDATE) + 1 + for i=quick_offset, #global.reactors, TICKS_PER_QUICK_UPDATE do quick_update_reactor(i) end - local index = (event.tick % TICKS_PER_UPDATE) + 1 - while index <= #global.reactors do - if global.reactors[index] then - update_reactor(index) - end - index = index + TICKS_PER_UPDATE + local offset = (event.tick % TICKS_PER_UPDATE) + 1 + for i=offset, #global.reactors, TICKS_PER_UPDATE do + update_reactor(i) end - - index = (event.tick % TICKS_PER_UPDATE) + 1 - while index <= #global.towers do - update_tower(index) - index = index + TICKS_PER_UPDATE + for i=offset, #global.towers, TICKS_PER_UPDATE do + update_tower(i) end end) diff --git a/prototypes/entities.lua b/prototypes/entities.lua index 860fdda..f64bbae 100644 --- a/prototypes/entities.lua +++ b/prototypes/entities.lua @@ -168,7 +168,7 @@ reactor_boiler = selection_box = {{-1.5,-1.5},{1.5,1.5}}, fluid_box = { - base_area = 50, + base_area = 200, pipe_covers = pipecoverspictures(), pipe_connections = { @@ -474,4 +474,4 @@ data:extend({ cooling_tower_steam, turbine, peak_turbine -}) \ No newline at end of file +})
I'm not sure if it's worth releasing an update at this stage, given how close 1.5 apparently is, but I'm more than happy for you to share your fix. If you want to zip it all up with the mod you're welcome to do that as well.
What's the performance like on Vegemeister's version?Optera wrote:Maybe you could provide an "official" dumbed down version for us potato users GotLag.