Ports /tg controller optimizations

https://github.com/tgstation/tgstation/pull/31092 - Fixes subsystems not returning a qdel hint
https://github.com/tgstation/tgstation/pull/31494 - In which the stoner one gets stoned and tries to address tick contention... again
https://github.com/tgstation/tgstation/pull/31950 - Removes an empty New()
https://github.com/tgstation/tgstation/pull/31951 - Logs subsystem shutdowns
This commit is contained in:
Leshana
2017-12-27 19:54:45 -05:00
parent c6e8184b58
commit a435d73450
8 changed files with 59 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
#define TICK_LIMIT_RUNNING 80 #define TICK_LIMIT_RUNNING 80
#define TICK_LIMIT_TO_RUN 78 #define TICK_LIMIT_TO_RUN 70
#define TICK_LIMIT_MC 70 #define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98 #define TICK_LIMIT_MC_INIT_DEFAULT 98

View File

@@ -8,3 +8,7 @@
//for when you need a reliable time number that doesn't depend on byond time. //for when you need a reliable time number that doesn't depend on byond time.
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK)) #define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers ) #define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )

View File

@@ -182,7 +182,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time var/starttime = world.time
. = 1 . = 1
while (world.time < endtime) while (world.time < endtime)
sleep(1) stoplag(1)
if (progress) if (progress)
progbar.update(world.time - starttime) progbar.update(world.time - starttime)
if(!user || !target) if(!user || !target)
@@ -229,7 +229,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time var/starttime = world.time
. = 1 . = 1
while (world.time < endtime) while (world.time < endtime)
sleep(1) stoplag(1)
if (progress) if (progress)
progbar.update(world.time - starttime) progbar.update(world.time - starttime)

View File

@@ -13,6 +13,12 @@
#define TimeOfGame (get_game_time()) #define TimeOfGame (get_game_time())
#define TimeOfTick (TICK_USAGE*0.01*world.tick_lag) #define TimeOfTick (TICK_USAGE*0.01*world.tick_lag)
#define TICK *world.tick_lag
#define TICKS *world.tick_lag
#define DS2TICKS(DS) (DS/world.tick_lag) // Convert deciseconds to ticks
#define TICKS2DS(T) (T TICKS) // Convert ticks to deciseconds
/proc/get_game_time() /proc/get_game_time()
var/global/time_offset = 0 var/global/time_offset = 0
var/global/last_time = 0 var/global/last_time = 0
@@ -111,13 +117,21 @@ var/round_start_time = 0
//Increases delay as the server gets more overloaded, //Increases delay as the server gets more overloaded,
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful //as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta,1)), 1) #define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1)
/proc/stoplag() //returns the number of ticks slept
/proc/stoplag(initial_delay)
if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
sleep(world.tick_lag)
return 1
if (!initial_delay)
initial_delay = world.tick_lag
. = 0 . = 0
var/i = 1 var/i = DS2TICKS(initial_delay)
do do
. += round(i*DELTA_CALC) . += CEILING(i*DELTA_CALC, 1)
sleep(i*world.tick_lag*DELTA_CALC) sleep(i*world.tick_lag*DELTA_CALC)
i *= 2 i *= 2
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
#undef DELTA_CALC

View File

@@ -12,7 +12,7 @@ var/datum/controller/master/Master = new()
name = "Master" name = "Master"
// Are we processing (higher values increase the processing delay by n ticks) // Are we processing (higher values increase the processing delay by n ticks)
var/processing = 1 var/processing = TRUE
// How many times have we ran // How many times have we ran
var/iteration = 0 var/iteration = 0
@@ -27,7 +27,7 @@ var/datum/controller/master/Master = new()
var/init_time var/init_time
var/tickdrift = 0 var/tickdrift = 0
var/sleep_delta var/sleep_delta = 1
var/make_runtime = 0 var/make_runtime = 0
@@ -77,7 +77,9 @@ var/datum/controller/master/Master = new()
sortTim(subsystems, /proc/cmp_subsystem_init) sortTim(subsystems, /proc/cmp_subsystem_init)
reverseRange(subsystems) reverseRange(subsystems)
for(var/datum/controller/subsystem/ss in subsystems) for(var/datum/controller/subsystem/ss in subsystems)
log_world("Shutting down [ss.name] subsystem...")
ss.Shutdown() ss.Shutdown()
log_world("Shutdown complete")
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart, // Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
// -1 if we encountered a runtime trying to recreate it // -1 if we encountered a runtime trying to recreate it
@@ -91,7 +93,7 @@ var/datum/controller/master/Master = new()
var/delay = 50 * ++Master.restart_count var/delay = 50 * ++Master.restart_count
Master.restart_timeout = world.time + delay Master.restart_timeout = world.time + delay
Master.restart_clear = world.time + (delay * 2) Master.restart_clear = world.time + (delay * 2)
Master.processing = 0 //stop ticking this one Master.processing = FALSE //stop ticking this one
try try
new/datum/controller/master() new/datum/controller/master()
catch catch
@@ -267,35 +269,45 @@ var/datum/controller/master/Master = new()
iteration = 1 iteration = 1
var/error_level = 0 var/error_level = 0
var/sleep_delta = 0 var/sleep_delta = 1
var/list/subsystems_to_check var/list/subsystems_to_check
//the actual loop. //the actual loop.
while (1) while (1)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag))) tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
var/starting_tick_usage = TICK_USAGE
if (processing <= 0) if (processing <= 0)
current_ticklimit = TICK_LIMIT_RUNNING current_ticklimit = TICK_LIMIT_RUNNING
sleep(10) sleep(10)
continue continue
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later //Anti-tick-contention heuristics:
// because sleeps are processed in the order received, so longer sleeps are more likely to run first //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
if (TICK_USAGE > TICK_LIMIT_MC) // (because sleeps are processed in the order received, longer sleeps are more likely to run first)
sleep_delta += 2 if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
sleep_delta *= 2
current_ticklimit = TICK_LIMIT_RUNNING * 0.5 current_ticklimit = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing + sleep_delta)) sleep(world.tick_lag * (processing * sleep_delta))
continue continue
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0) //Byond resumed us late. assume it might have to do the same next tick
if (last_run + (world.tick_lag * processing) > world.time) if (last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
sleep_delta += 1
if (TICK_USAGE > (TICK_LIMIT_MC*0.5))
sleep_delta += 1 sleep_delta += 1
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) //we ran 3/4 of the way into the tick
sleep_delta += 1
//debug
if (make_runtime) if (make_runtime)
var/datum/controller/subsystem/SS var/datum/controller/subsystem/SS
SS.can_fire = 0 SS.can_fire = 0
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time)) if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe. new/datum/controller/failsafe() // (re)Start the failsafe.
//now do the actual stuff
if (!queue_head || !(iteration % 3)) if (!queue_head || !(iteration % 3))
var/checking_runlevel = current_runlevel var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel) if(cached_runlevel != checking_runlevel)
@@ -312,6 +324,7 @@ var/datum/controller/master/Master = new()
subsystems_to_check = current_runlevel_subsystems subsystems_to_check = current_runlevel_subsystems
else else
subsystems_to_check = tickersubsystems subsystems_to_check = tickersubsystems
if (CheckQueue(subsystems_to_check) <= 0) if (CheckQueue(subsystems_to_check) <= 0)
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing") log_world("MC: SoftReset() failed, crashing")
@@ -342,8 +355,10 @@ var/datum/controller/master/Master = new()
iteration++ iteration++
last_run = world.time last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc. current_ticklimit = TICK_LIMIT_RUNNING
sleep(world.tick_lag * (processing + sleep_delta)) if (processing * sleep_delta <= world.tick_lag)
current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
sleep(world.tick_lag * (processing * sleep_delta))

View File

@@ -31,8 +31,7 @@
var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
//Do not override //Do not override
/datum/controller/subsystem/New() ///datum/controller/subsystem/New()
return
// Used to initialize the subsystem BEFORE the map has loaded // Used to initialize the subsystem BEFORE the map has loaded
// Called AFTER Recover if that is called // Called AFTER Recover if that is called
@@ -66,7 +65,7 @@
can_fire = 0 can_fire = 0
flags |= SS_NO_FIRE flags |= SS_NO_FIRE
Master.subsystems -= src Master.subsystems -= src
return ..()
//Queue it to run. //Queue it to run.
// (we loop thru a linked list until we get to the end or find the right point) // (we loop thru a linked list until we get to the end or find the right point)

View File

@@ -296,8 +296,7 @@
if (holder) if (holder)
sleep(1) sleep(1)
else else
sleep(5) stoplag(5)
stoplag()
/client/proc/last_activity_seconds() /client/proc/last_activity_seconds()
return inactivity / 10 return inactivity / 10

View File

@@ -1165,7 +1165,7 @@
//They ran away! //They ran away!
else else
ai_log("AttackTarget() out of range!",3) ai_log("AttackTarget() out of range!",3)
sleep(1) // Unfortunately this is needed to protect from ClosestDistance() sometimes not updating fast enough to prevent an infinite loop. stoplag(1) // Unfortunately this is needed to protect from ClosestDistance() sometimes not updating fast enough to prevent an infinite loop.
handle_stance(STANCE_ATTACK) handle_stance(STANCE_ATTACK)
return 0 return 0