perf: hak-audit — spredte timere, fælles cache (markør->station, salgssteder, identify, Wait-punkter, lagre, positioner), bygningsliste 10 s, profiler, simulering (v1.28.0.0)

Audit (2 reviewere) + simulering bench/bench_hak.lua: værste frame 124,7 ms -> 0,9 ms.
- Holdende vognes genplanlægning (5 s) spredes tilfældigt over hele intervallet (før: alle i samme frame).
- ADCache: korttids-cache m. spredt udløb (+0-50 %), kun aktiv i spillet; stationsopslag 30 s.
- Bygningsliste 10 s (før 1 s: bygninger x markører hvert sekund). Markarbejde-tick forskudt 0,5 s fra menuen.
- ADProfiler: logger top-6 langsomste indgange pr. minut ved > 2 ms.
This commit is contained in:
2026-09-26 11:24:05 +02:00
parent 8a18187103
commit 6fd917caca
16 changed files with 292 additions and 20 deletions
+10
View File
@@ -0,0 +1,10 @@
# Simuleringer
`bench_hak.lua` genskaber "hak hvert 5. sekund" uden spillet: falsk verden i brugerens proportioner
(13.550 waypoints, 600 markører, 120 stationer, 40 køretøjer, 15 holdende vogne) og modulernes rigtige kode.
luajit bench/bench_hak.lua FS25_ADSmartPickup # nuværende kode
git worktree add /tmp/old <commit>; luajit bench/bench_hak.lua /tmp/old/FS25_ADSmartPickup # sammenlign
Resultat 26/9: værste frame 124,7 ms (v1.27.3) -> 0,9 ms (v1.28.0), 0 tunge frames efter opvarmning.
luajit er hurtigere end spillets Lua: forholdet før/efter er det der tæller, ikke de absolutte ms.
+103
View File
@@ -0,0 +1,103 @@
-- Simulering af "hak hvert 5. sekund" (kør: luajit bench/bench_hak.lua <mappe med modulerne>)
-- Falsk verden i brugerens proportioner: 13.550 waypoints, 600 markører (150 Wait), 120 stationer med 3
-- triggere, 40 køretøjer, 15 holdende vogne der genplanlægger hvert 5. s. Kører modulernes RIGTIGE kode
-- (Wait-punktsøgning, markør -> station, salgssteder) frame for frame og måler det værste frame.
-- luajit er hurtigere end spillets Lua; FORHOLDET mellem før og efter er det der tæller.
local dir = arg[1] or "FS25_ADSmartPickup"
math.randomseed(42)
local function noop() end
Logging = {info = noop, warning = noop}
MathUtil = {vector2Length = function(x, z) return math.sqrt(x * x + z * z) end}
function entityExists() return true end
local nodes = {}
function getWorldTranslation(node) local p = nodes[node]; return p[1], 0, p[2] end
local simTime = 0
g_currentMission = {time = 0, getFarmId = function() return 1 end, accessHandler = {canFarmAccess = function() return true end}}
function netGetTime() return os.clock() * 1000 end
addModEventListener = noop
Utils = {prependedFunction = function(a) return a end}
-- vejnet og markører
local wayPoints, markers = {}, {}
for id = 1, 13550 do
local x, z = (id % 120) * 60 - 3600, math.floor(id / 120) * 60 - 3600
wayPoints[id] = {id = id, x = x, z = z, out = {id + 1 <= 13550 and id + 1 or 1}, incoming = {id > 1 and id - 1 or 13550}}
end
for index = 1, 600 do
local id = index * 22
table.insert(markers, {id = id, markerIndex = index, name = (index <= 150 and "Wait " or "Marker ") .. index, group = index <= 150 and "Cow Wait" or "All"})
end
-- stationer ved hver 5. markør, 3 triggere hver
local stations = {}
for index = 1, 120 do
local wp = wayPoints[markers[150 + index].id]
local triggers = {}
for t = 1, 3 do
local node = #nodes + 1
nodes[node] = {wp.x + t, wp.z + t}
table.insert(triggers, {exactFillRootNode = node})
end
table.insert(stations, {unloadTriggers = triggers, owningPlaceable = {}, getOwnerFarmId = function() return 1 end, name = "S" .. index,
isa = function() return false end, getIsFillTypeAllowed = function() return true end})
end
g_currentMission.storageSystem = {getUnloadingStations = function() return stations end, getLoadingStations = function() return {} end}
g_currentMission.placeableSystem = {placeables = {}}
-- køretøjer
local vehicles = {}
for index = 1, 40 do
local node = #nodes + 1
nodes[node] = {math.random(-3600, 3600), math.random(-3600, 3600)}
table.insert(vehicles, {rootNode = node, spec_motorized = {}, getChildVehicles = function() return {} end})
end
g_currentMission.vehicleSystem = {vehicles = vehicles}
local adEnv = {ADGraphManager = {
getMapMarkers = function() return markers end,
getWayPointById = function(_, id) return wayPoints[id] end,
getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end,
getPathTo = function(_, _, id) return {{id = id}} end,
}}
for _, file in ipairs({"adCache.lua", "adSupplyPlanner.lua", "adWaitPool.lua", "adPrices.lua", "adSmartPickup.lua"}) do
local chunk = loadfile(dir .. "/" .. file)
if chunk ~= nil then pcall(chunk) end
end
if ADCache ~= nil then ADCache.enabled = true end
ADPrices = ADPrices or {}
ADPrices.isSellingStation = ADPrices.isSellingStation or function() return true end
-- én genplanlægning for en holdende vogn: Wait-punkt + stationsopslag over alle markører (salgssteder/lager)
local function replan(vehicle)
ADWaitPool.pickFree(adEnv, vehicle, "Cow Wait", function() return true end)
for _, marker in ipairs(markers) do
ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, 1)
end
end
local t0 = os.clock()
replan(vehicles[1])
local single = (os.clock() - t0) * 1000
-- 30 s med 60 fps; 15 holdende vogne. Gammel kode: alle timere starter på 0. Ny: jitter hvis koden har det.
local jitter = ADCache ~= nil and 5000 or 0 -- ny kode spreder vognenes genplanlægning over 0-5 s (adOutbound CHECK_JITTER_MS)
local timers = {}
for index = 1, 15 do timers[index] = jitter > 0 and math.random(0, jitter) or 0 end
local worst, steady, frames = 0, 0, 0
for frame = 1, 60 * 60 do
simTime = simTime + 16.7
g_currentMission.time = simTime
local started = os.clock()
for index = 1, 15 do
timers[index] = timers[index] + 16.7
if timers[index] >= 5000 then
timers[index] = 0
replan(vehicles[index])
end
end
local spent = (os.clock() - started) * 1000
if spent > worst then worst = spent end
if simTime > 12000 and spent > steady then steady = spent end
if simTime > 12000 and spent > 3 then heavy = (heavy or 0) + 1; heavyAt = heavyAt or {}; if #heavyAt < 8 then table.insert(heavyAt, string.format("%.1fs:%.1fms", simTime / 1000, spent)) end end
frames = frames + 1
end
print(" tunge frames (>3 ms) efter opvarmning:", heavy or 0, table.concat(heavyAt or {}, " "))
print(string.format("%-12s én genplanlægning %.2f ms | værste frame (60 s): %.2f ms | værste efter opvarmning: %.2f ms", dir:find("old") and "FØR" or "EFTER", single, worst, steady))