From c8bb13d7c2966dadafc789bb6dc10639a853105e Mon Sep 17 00:00:00 2001 From: MrStonedOne Date: Mon, 9 Oct 2017 01:21:43 -0700 Subject: [PATCH] In which the stoner one gets stoned and tries to address tick contention... again Tick contention is when the mc, sleep()/spawns(), and byond internal processing fight each other for game tick time. Usually in an unproductive way that wastes cpu cycles and reduces the effective amount of game tick to go around. Tweaked the anti-tick contention heuristics of the MC a touch. Fixed an incorrect operator in the mc's anti-tick contention heuristics causing it to apply in times of no lag rather then times of lag. The mc's anti-tick contention heuristics now plays better with the high pop processing mode. We no longer reserve the tail end of a tick for the mc to have if the mc doesn't plan to run next tick because of high pop mode or anti-tick contention heuristics. stoplag() can now be given an initial delay allowing it to act like a smarter sleep (in that it sleeps for longer if the server is overwhelmed. All short sleeps that only existed for performance reason and had no game play, visual/audio, or balance reasons behind their timing were converted to stoplag(). --- code/__DEFINES/_tick.dm | 2 +- code/__DEFINES/time.dm | 6 ++- code/__HELPERS/mobs.dm | 6 +-- code/__HELPERS/unsorted.dm | 19 +++++---- code/controllers/master.dm | 41 ++++++++++++------- code/datums/browser.dm | 2 +- code/datums/explosion.dm | 4 +- code/datums/martial/wrestling.dm | 4 +- code/datums/mind.dm | 2 +- .../clock_cult/clock_effects/clock_sigils.dm | 7 ++-- .../gamemodes/clock_cult/clock_scripture.dm | 2 +- code/game/machinery/dance_machine.dm | 4 +- code/game/objects/items.dm | 2 +- code/game/objects/items/storage/storage.dm | 4 +- code/game/objects/structures/barsigns.dm | 2 +- code/game/turfs/turf.dm | 2 +- code/modules/client/asset_cache.dm | 6 +-- code/modules/client/client_procs.dm | 5 +-- .../hostile/megafauna/blood_drunk_miner.dm | 2 +- code/modules/projectiles/projectile.dm | 2 +- .../spells/spell_types/touch_attacks.dm | 2 +- 21 files changed, 72 insertions(+), 54 deletions(-) diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 93ac8ed70d6..20eb648fe62 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -1,5 +1,5 @@ #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_INIT_DEFAULT 98 diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 28017de85c8..987d52e0cd0 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -20,4 +20,8 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using #define HOURS MINUTES*60 -#define TICKS *world.tick_lag \ No newline at end of file +#define TICKS *world.tick_lag + +#define DS2TICKS(DS) (DS/world.tick_lag) + +#define TICKS2DS(T) (T TICKS) \ No newline at end of file diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 56829a29d0d..c8ca87ce0c3 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -234,7 +234,7 @@ Proc for attack log creation, because really why not var/starttime = world.time . = 1 while (world.time < endtime) - stoplag() + stoplag(1) if (progress) progbar.update(world.time - starttime) if(QDELETED(user) || QDELETED(target)) @@ -295,7 +295,7 @@ Proc for attack log creation, because really why not var/starttime = world.time . = 1 while (world.time < endtime) - stoplag() + stoplag(1) if (progress) progbar.update(world.time - starttime) @@ -350,7 +350,7 @@ Proc for attack log creation, because really why not . = 1 mainloop: while(world.time < endtime) - sleep(1) + stoplag(1) if(progress) progbar.update(world.time - starttime) if(QDELETED(user) || !targets) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index de9f4e1d1c8..ed4a0d46241 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -505,10 +505,10 @@ Turf and target are separate in case you want to teleport some distance from a t /atom/proc/GetAllContents(list/output=list()) . = output - output += src - for(var/i in 1 to contents.len) - var/atom/thing = contents[i] - thing.GetAllContents(output) + output += src + for(var/i in 1 to contents.len) + var/atom/thing = contents[i] + thing.GetAllContents(output) //Step-towards method of determining whether one atom can see another. Similar to viewers() /proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. @@ -1226,16 +1226,19 @@ proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) //Increases delay as the server gets more overloaded, //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 - var/i = 1 + var/i = DS2TICKS(initial_delay) do - . += round(i*DELTA_CALC) + . += Ceiling(i*DELTA_CALC) sleep(i*world.tick_lag*DELTA_CALC) i *= 2 while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index b92853a2c47..5607dde491c 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -33,7 +33,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/init_time var/tickdrift = 0 - var/sleep_delta + var/sleep_delta = 1 var/make_runtime = 0 @@ -53,7 +53,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/restart_clear = 0 var/static/restart_timeout = 0 var/static/restart_count = 0 - + //current tick limit, assigned before running a subsystem. //used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits var/static/current_ticklimit = TICK_LIMIT_RUNNING @@ -276,35 +276,45 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration = 1 var/error_level = 0 - var/sleep_delta = 0 + var/sleep_delta = 1 var/list/subsystems_to_check //the actual loop. + while (1) 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) current_ticklimit = TICK_LIMIT_RUNNING sleep(10) continue - //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later - // because sleeps are processed in the order received, so longer sleeps are more likely to run first - if (TICK_USAGE > TICK_LIMIT_MC) - sleep_delta += 2 + //Anti-tick-contention heuristics: + //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later. + // (because sleeps are processed in the order received, longer sleeps are more likely to run first) + 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 - sleep(world.tick_lag * (processing + sleep_delta)) + sleep(world.tick_lag * (processing * sleep_delta)) continue - sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0) - if (last_run + (world.tick_lag * processing) > world.time) - sleep_delta += 1 - if (TICK_USAGE > (TICK_LIMIT_MC*0.5)) + //Byond resumed us late. assume it might have to do the same next tick + if (last_run + Ceiling(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time) 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) var/datum/controller/subsystem/SS SS.can_fire = 0 + if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time)) new/datum/controller/failsafe() // (re)Start the failsafe. + + //now do the actual stuff if (!queue_head || !(iteration % 3)) var/checking_runlevel = current_runlevel if(cached_runlevel != checking_runlevel) @@ -321,6 +331,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new subsystems_to_check = current_runlevel_subsystems else subsystems_to_check = tickersubsystems + if (CheckQueue(subsystems_to_check) <= 0) if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) log_world("MC: SoftReset() failed, crashing") @@ -351,8 +362,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration++ last_run = world.time 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. - sleep(world.tick_lag * (processing + sleep_delta)) + current_ticklimit = TICK_LIMIT_RUNNING + 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)) diff --git a/code/datums/browser.dm b/code/datums/browser.dm index b6738d5ad63..31e8daadb25 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -177,7 +177,7 @@ /datum/browser/alert/proc/wait() while (opentime && selectedbutton <= 0 && (!timeout || opentime+timeout > world.time)) - stoplag() + stoplag(1) /datum/browser/alert/Topic(href,href_list) if (href_list["close"] || !user || !user.client) diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index 9869b43d8f8..b9ea00da222 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -61,14 +61,14 @@ GLOBAL_LIST_EMPTY(explosions) flash_range = min(GLOB.MAX_EX_FLASH_RANGE, flash_range) flame_range = min(GLOB.MAX_EX_FLAME_RANGE, flame_range) - //DO NOT REMOVE THIS SLEEP, IT BREAKS THINGS + //DO NOT REMOVE THIS STOPLAG, IT BREAKS THINGS //not sleeping causes us to ex_act() the thing that triggered the explosion //doing that might cause it to trigger another explosion //this is bad //I would make this not ex_act the thing that triggered the explosion, //but everything that explodes gives us their loc or a get_turf() //and somethings expect us to ex_act them so they can qdel() - sleep(1) //tldr, let the calling proc call qdel(src) before we explode + stoplag() //tldr, let the calling proc call qdel(src) before we explode EX_PREPROCESS_EXIT_CHECK diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index ae7146ca33f..ac9bfcbab5a 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -199,7 +199,7 @@ set waitfor = FALSE if (D) animate(D, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0) - sleep (15) + sleep(15) if (D) animate(D, transform = null, time = 1, loop = 0) @@ -258,7 +258,7 @@ D.pixel_y = 0 return 0 - sleep (1) + sleep(1) if (A && D) A.pixel_x = 0 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index c3764105c9c..5d9d05394d9 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1294,7 +1294,7 @@ if (istype(M)) for(var/datum/disease/transformation/jungle_fever/JF in M.viruses) JF.cure(0) - sleep(0) //because deleting of virus is doing throught spawn(0) //What + stoplag() //because deleting of virus is doing throught spawn(0) //What log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm index d5b9ce44011..3091d6071a1 100644 --- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm +++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm @@ -123,10 +123,9 @@ if(glow_type) glow = new glow_type(get_turf(src)) animate(glow, alpha = 255, time = convert_time) - var/I = 0 - while(I < convert_time && get_turf(L) == get_turf(src)) - I++ - sleep(1) + var/end_time = world.time+convert_time + while(world.time < end_time && get_turf(L) == get_turf(src)) + stoplag(1) if(get_turf(L) != get_turf(src)) if(glow) qdel(glow) diff --git a/code/game/gamemodes/clock_cult/clock_scripture.dm b/code/game/gamemodes/clock_cult/clock_scripture.dm index 8e8cd912e77..049a49f5f4b 100644 --- a/code/game/gamemodes/clock_cult/clock_scripture.dm +++ b/code/game/gamemodes/clock_cult/clock_scripture.dm @@ -319,7 +319,7 @@ Applications: 8 servants, 3 caches, and 100 CV qdel(progbar) else progbar.update(end_time - world.time) - sleep(1) + stoplag(1) if(slab) if(slab.slab_ability) successful = slab.slab_ability.successful diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index f919bdbab89..4c90f3ae4e4 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -381,7 +381,7 @@ initial_matrix = matrix(M.transform) initial_matrix.Translate(-3,0) animate(M, transform = initial_matrix, time = 1, loop = 0) - sleep (1) + sleep(1) M.lying_fix() @@ -428,7 +428,7 @@ initial_matrix = matrix(M.transform) initial_matrix.Translate(-3,0) animate(M, transform = initial_matrix, time = 1, loop = 0) - sleep (1) + sleep(1) M.lying_fix() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 96803032d56..3f50bc2d5bb 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -342,7 +342,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/datum/progressbar/progress = new(user, len, loc) while (do_after(user, 10, TRUE, S, FALSE, CALLBACK(src, .proc/handle_mass_pickup, S, things, loc, rejections, progress))) - sleep(1) + stoplag(1) qdel(progress) diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm index 59a339a6fb2..5d0b51e9019 100644 --- a/code/game/objects/items/storage/storage.dm +++ b/code/game/objects/items/storage/storage.dm @@ -88,7 +88,7 @@ var/list/things = src_object.contents.Copy() var/datum/progressbar/progress = new(user, things.len, src) while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(src, .proc/handle_mass_item_insertion, things, src_object, user, progress))) - sleep(1) + stoplag(1) qdel(progress) orient2hud(user) src_object.orient2hud(user) @@ -495,7 +495,7 @@ var/list/things = contents.Copy() var/datum/progressbar/progress = new(usr, things.len, T) while (do_after(usr, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress))) - sleep(1) + stoplag(1) qdel(progress) /obj/item/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress) diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm index f9e6785e44a..6fda0f6a40d 100644 --- a/code/game/objects/structures/barsigns.dm +++ b/code/game/objects/structures/barsigns.dm @@ -120,7 +120,7 @@ return emagged = TRUE to_chat(user, "You emag the barsign. Takeover in progress...") - sleep(100) //10 seconds + sleep(10 SECONDS) set_sign(new /datum/barsign/hiddensigns/syndibarsign) req_access = list(ACCESS_SYNDICATE) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 33d74892290..11bf302c7d2 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -331,7 +331,7 @@ var/list/things = src_object.contents.Copy() var/datum/progressbar/progress = new(user, things.len, src) while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /obj/item/storage.proc/mass_remove_from_storage, src, things, progress))) - sleep(1) + stoplag(1) qdel(progress) return TRUE diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index cda7b3dccf4..c411f049186 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -61,7 +61,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the var/t = 0 var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. + stoplag(1) // Lock up the caller until this is received. t++ if(client) @@ -112,7 +112,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the var/t = 0 var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. + stoplag(1) // Lock up the caller until this is received. t++ if(client) @@ -131,7 +131,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the if (register_asset) register_asset(file,files[file]) send_asset(client,file) - sleep(0) //queuing calls like this too quickly can cause issues in some client versions + stoplag(0) //queuing calls like this too quickly can cause issues in some client versions //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. //if it's an icon or something be careful, you'll have to copy it before further use. diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 886477fa965..f80e2329c76 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -619,10 +619,9 @@ GLOBAL_LIST(external_rsc_urls) /client/Stat() . = ..() if (holder) - sleep(1) + stoplag(1) else - sleep(5) - stoplag() + stoplag(5) //send resources to the client. It's here in its own proc so we can move it around easiliy if need be /client/proc/send_resources() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 7b67fc1b3b5..b79ead4168c 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -161,7 +161,7 @@ Difficulty: Medium /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop() if(next_move <= world.time) - sleep(1) + stoplag(1) .() //retry return sleep((next_move - world.time) * 1.5) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 937326a60b6..fcd41833cb6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -213,7 +213,7 @@ while(loc) if(paused) next_run = world.time - sleep(1) + stoplag(1) continue if((!( current ) || loc == current)) diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm index 06a699e54f7..46563da53d9 100644 --- a/code/modules/spells/spell_types/touch_attacks.dm +++ b/code/modules/spells/spell_types/touch_attacks.dm @@ -21,7 +21,7 @@ return 0 while(attached_hand) //hibernate untill the spell is actually used charge_counter = 0 - sleep(1) + stoplag(1) /obj/effect/proc_holder/spell/targeted/touch/proc/ChargeHand(mob/living/carbon/user) attached_hand = new hand_path(src)