Performance improvements: Don't clear paths unless absolutely necessary

instead delete path elements that are too far from the train one-by-one
Also when switching a switch or changing rails, only clear train paths of trains that are nearby.
master
orwell96 2017-05-03 16:31:13 +02:00
parent 0c7144bcc4
commit 28717b4d65
6 changed files with 118 additions and 34 deletions

Binary file not shown.

View File

@ -93,7 +93,7 @@ advtrains.register_tracks("default", {
after_place_node=apn_func,
after_dig_node=function(pos)
return advtrains.pcall(function()
advtrains.invalidate_all_paths()
advtrains.invalidate_all_paths(pos)
advtrains.ndb.clear(pos)
local pts=minetest.pos_to_string(pos)
atc.controllers[pts]=nil

View File

@ -37,7 +37,7 @@ advtrains.modpath = minetest.get_modpath("advtrains")
function advtrains.print_concat_table(a)
local str=""
local stra=""
for i=1,50 do
for i=1,10 do
t=a[i]
if t==nil then
stra=stra.."nil "
@ -67,9 +67,12 @@ end
atprint=function() end
if minetest.setting_getbool("advtrains_debug") then
atprint=function(t, ...)
local context=advtrains.atprint_context_tid
if not context then context="" end
--if context~="4527" then return end
local text=advtrains.print_concat_table({t, ...})
minetest.log("action", "[advtrains]"..text)
minetest.chat_send_all("[advtrains]"..text)
minetest.log("action", "[advtrains]"..context..">"..text)
minetest.chat_send_all("[advtrains]"..context..">"..text)
end
end
atwarn=function(t, ...)
@ -171,7 +174,8 @@ end
advtrains.avt_save = function()
--atprint("saving")
advtrains.invalidate_all_paths()
--No more invalidating.
--Instead, remove path a.s.o from the saved table manually
-- update wagon saves
for _,wagon in pairs(minetest.luaentities) do
@ -195,10 +199,34 @@ advtrains.avt_save = function()
end
end
local tmp_trains={}
for id, train in pairs(advtrains.trains) do
--first, deep_copy the train
local v=advtrains.merge_tables(train)
--then invalidate
if v.index then
v.restore_add_index=v.index-math.floor(v.index+0.5)
end
v.path=nil
v.path_dist=nil
v.index=nil
v.end_index=nil
v.min_index_on_track=nil
v.max_index_on_track=nil
v.path_extent_min=nil
v.path_extent_max=nil
v.detector_old_index=nil
v.detector_old_end_index=nil
--then save it
tmp_trains[id]=v
end
--versions:
-- 1 - Initial new save format.
local save_tbl={
trains = advtrains.trains,
trains = tmp_trains,
wagon_save = advtrains.wagon_save,
ptmap = advtrains.player_to_train_mapping,
atc = advtrains.atc.save_data(),

View File

@ -263,13 +263,13 @@ function advtrains.register_tracks(tracktype, def, preset)
local rcswitchfunc=function(pos, node, player)
if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
advtrains.invalidate_all_paths()
advtrains.invalidate_all_paths(pos)
end
end
local switchfunc=function(pos, node, newstate)
if newstate~=is_state then
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
advtrains.invalidate_all_paths()
advtrains.invalidate_all_paths(pos)
end
end
local mesec

View File

@ -55,12 +55,16 @@ advtrains.mainloop_trainlogic=function(dtime)
local t=os.clock()
advtrains.detector.on_node={}
for k,v in pairs(advtrains.trains) do
advtrains.atprint_context_tid=sid(k)
advtrains.train_step_a(k, v, dtime)
end
for k,v in pairs(advtrains.trains) do
advtrains.atprint_context_tid=sid(k)
advtrains.train_step_b(k, v, dtime)
end
advtrains.atprint_context_tid=nil
atprintbm("trainsteps", t)
endstep()
end
@ -140,7 +144,7 @@ function advtrains.train_step_a(id, train, dtime)
end
--- 2. prepare initial path and index if needed ---
if not train.index then train.index=0 end
if not train.path or #train.path<2 then
if not train.path then
if not train.last_pos then
--no chance to recover
atprint("train hasn't saved last-pos, removing train.")
@ -224,36 +228,43 @@ function advtrains.train_step_a(id, train, dtime)
train.tarvelocity=0
end
--- 3a. this can be useful for debugs/warnings and is used for check_trainpartload ---
local t_info, train_pos=sid(id), train.path[math.floor(train.index)]
if train_pos then
t_info=t_info.." @"..minetest.pos_to_string(train_pos)
end
--apply off-track handling:
local front_off_track=train.max_index_on_track and train.index>train.max_index_on_track
local back_off_track=train.min_index_on_track and train.end_index<train.min_index_on_track
local pprint
if front_off_track and back_off_track then--allow movement in both directions
if train.tarvelocity>1 then
train.tarvelocity=1
atwarn("Train",sid(id)," is off track at both ends. Clipping velocity to 1")
atwarn("Train",t_info,"is off track at both ends. Clipping velocity to 1")
pprint=true
end
elseif front_off_track then--allow movement only backward
if train.movedir==1 and train.tarvelocity>0 then
train.tarvelocity=0
atwarn("Train",sid(id)," is off track. Trying to drive further out. Velocity clipped to 0")
atwarn("Train",t_info,"is off track. Trying to drive further out. Velocity clipped to 0")
pprint=true
end
if train.movedir==-1 and train.tarvelocity>1 then
train.tarvelocity=1
atwarn("Train",sid(id)," is off track. Velocity clipped to 1")
atwarn("Train",t_info,"is off track. Velocity clipped to 1")
pprint=true
end
elseif back_off_track then--allow movement only forward
if train.movedir==-1 and train.tarvelocity>0 then
train.tarvelocity=0
atwarn("Train",sid(id)," is off track. Trying to drive further out. Velocity clipped to 0")
atwarn("Train",t_info,"is off track. Trying to drive further out. Velocity clipped to 0")
pprint=true
end
if train.movedir==1 and train.tarvelocity>1 then
train.tarvelocity=1
atwarn("Train",sid(id)," is off track. Velocity clipped to 1")
atwarn("Train",t_info,"is off track. Velocity clipped to 1")
pprint=true
end
end
@ -322,7 +333,7 @@ function advtrains.train_step_a(id, train, dtime)
--why this is an extra function, see under 3.
advtrains.pathpredict(id, train, true)
--make pos/yaw available for possible recover calls
--- 5a. make pos/yaw available for possible recover calls ---
if train.max_index_on_track<train.index then --whoops, train went too far. the saved position will be the last one that lies on a track, and savedpos_off_track_index_offset will hold how far to go from here
train.savedpos_off_track_index_offset=train.index-train.max_index_on_track
train.last_pos=train.path[train.max_index_on_track]
@ -339,6 +350,36 @@ function advtrains.train_step_a(id, train, dtime)
train.last_pos_prev=train.path[math.floor(train.index-0.5)]
end
--- 5b. Remove path items that are no longer used ---
-- Necessary since path items are no longer invalidated in save steps
local path_pregen_keep=20
local offtrack_keep=4
local gen_front_keep= path_pregen_keep
local gen_back_keep= - train.trainlen - path_pregen_keep
local delete_min=math.min(train.max_index_on_track - offtrack_keep, math.floor(train.index)+gen_back_keep)
local delete_max=math.max(train.min_index_on_track + offtrack_keep, math.floor(train.index)+gen_front_keep)
if train.path_extent_min<delete_min then
atprint(sid(id),"clearing path min ",train.path_extent_min," to ",delete_min)
for i=train.path_extent_min,delete_min-1 do
train.path[i]=nil
train.path_dist[i]=nil
end
train.path_extent_min=delete_min
train.min_index_on_track=math.max(train.min_index_on_track, delete_min)
end
if train.path_extent_max>delete_max then
atprint(sid(id),"clearing path max ",train.path_extent_max," to ",delete_max)
train.path_dist[delete_max]=nil
for i=delete_max+1,train.path_extent_max do
train.path[i]=nil
train.path_dist[i]=nil
end
train.path_extent_max=delete_max
train.max_index_on_track=math.min(train.max_index_on_track, delete_max)
end
--- 6. update node coverage ---
-- when paths get cleared, the old indices set above will be up-to-date and represent the state in which the last run of this code was made
@ -406,7 +447,7 @@ function advtrains.train_step_a(id, train, dtime)
train.check_trainpartload=(train.check_trainpartload or 0)-dtime
local node_range=(math.max((minetest.setting_get("active_block_range") or 0),1)*16)
if train.check_trainpartload<=0 then
local ori_pos=advtrains.get_real_index_position(path, train.index) --not much to calculate
local ori_pos=train_pos --see 3a.
--atprint("[train "..id.."] at "..minetest.pos_to_string(vector.round(ori_pos)))
local should_check=false
@ -429,6 +470,7 @@ end
--about regular: Used by 1. to ensure path gets generated far enough, since end index is not known at this time.
function advtrains.pathpredict(id, train, regular)
--TODO duplicate code under 5b.
local path_pregen=10
local gen_front= path_pregen
@ -440,7 +482,7 @@ function advtrains.pathpredict(id, train, regular)
local maxn=train.path_extent_max or 0
while maxn < gen_front do--pregenerate
--atprint("maxn conway for ",maxn,minetest.pos_to_string(path[maxn]),maxn-1,minetest.pos_to_string(path[maxn-1]))
atprint("maxn conway for ",maxn,minetest.pos_to_string(train.path[maxn]),maxn-1,minetest.pos_to_string(train.path[maxn-1]))
local conway=advtrains.conway(train.path[maxn], train.path[maxn-1], train.drives_on)
if conway then
train.path[maxn+1]=conway
@ -458,7 +500,7 @@ function advtrains.pathpredict(id, train, regular)
local minn=train.path_extent_min or -1
while minn > gen_back do
--atprint("minn conway for ",minn,minetest.pos_to_string(path[minn]),minn+1,minetest.pos_to_string(path[minn+1]))
atprint("minn conway for ",minn,minetest.pos_to_string(train.path[minn]),minn+1,minetest.pos_to_string(train.path[minn+1]))
local conway=advtrains.conway(train.path[minn], train.path[minn+1], train.drives_on)
if conway then
train.path[minn-1]=conway
@ -858,23 +900,37 @@ function advtrains.get_train_at_pos(pos)
return advtrains.detector.on_node[ph]
end
function advtrains.invalidate_all_paths()
--atprint("invalidating all paths")
function advtrains.invalidate_all_paths(pos)
--if a position is given, only invalidate inside a radius to save performance
local inv_radius=50
atprint("invalidating all paths")
for k,v in pairs(advtrains.trains) do
if v.index then
v.restore_add_index=v.index-math.floor(v.index+0.5)
local exec=true
if pos and v.path and v.index and v.end_index then
--start and end pos of the train
local cmp1=v.path[math.floor(v.index)]
local cmp2=v.path[math.floor(v.end_index)]
if vector.distance(pos, cmp1)>inv_radius and vector.distance(pos, cmp2)>inv_radius then
exec=false
end
end
if exec then
--TODO duplicate code in init.lua avt_save()!
if v.index then
v.restore_add_index=v.index-math.floor(v.index+0.5)
end
v.path=nil
v.path_dist=nil
v.index=nil
v.end_index=nil
v.min_index_on_track=nil
v.max_index_on_track=nil
v.path_extent_min=nil
v.path_extent_max=nil
v.detector_old_index=nil
v.detector_old_end_index=nil
end
v.path=nil
v.path_dist=nil
v.index=nil
v.end_index=nil
v.min_index_on_track=nil
v.max_index_on_track=nil
v.path_extent_min=nil
v.path_extent_max=nil
v.detector_old_index=nil
v.detector_old_end_index=nil
end
end

View File

@ -49,7 +49,7 @@ function ac.getform(pos, meta_p)
end
function ac.after_dig_node(pos, node, player)
advtrains.invalidate_all_paths()
advtrains.invalidate_all_paths(pos)
advtrains.ndb.clear(pos)
local ph=minetest.pos_to_string(pos)
ac.nodes[ph]=nil