mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
[MIRROR] Experiment with holding hard references to objects being qdeleted in 515 (saves 1.1+ seconds on init times, more on prod) [MDB IGNORE] (#18361)
* Experiment with holding hard references to objects being qdeleted in 515 (saves 1.1+ seconds on init times, more on prod) (#72033) ## About The Pull Request Adds `EXPERIMENT_515_QDEL_HARD_REFERENCE`, which will queue to the GC subsystem using hard references rather than `\ref`. This is only possible in 515 because of the new `refcount` proc. `\ref` is very very slow and has some nasty knock on effects, so removing its usages where possible is good. This is an explicit opt in define because I want to give us the ability to test 515 on live while only testing 515 itself, not our experimental changes. We have a few more of these we want to do so I made a separate file for them. They're auto-defined in unit tests so we see them with the alternate test runner. In a perfect world we'd test both on and off, but eh. Closes https://github.com/tgstation/dev-cycles-initiative/issues/10 * Experiment with holding hard references to objects being qdeleted in 515 (saves 1.1+ seconds on init times, more on prod) * fix missed underbarrels * HEV radio * Keeps gc_destroyed from getting updated on every step thru the gc queue. (#72401) Keeps gc_destroyed from getting updated on every step thru the gc queue. Fixes logic that assumed gc_destroyed is the time the object first qdel'ed. it used to get updated on each stage of the garbage controller and there are 3 stages. Added list index defines for the inner gc item list. * test fix * final fix Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: tastyfish <crazychris32@gmail.com> Co-authored-by: Kyle Spier-Swenson <kyleshome@gmail.com>
This commit is contained in:
co-authored by
Mothblocks
tastyfish
Kyle Spier-Swenson
parent
d2406cf6bd
commit
ef1ad8331a
@@ -22,6 +22,8 @@ SUBSYSTEM_DEF(atoms)
|
||||
/// Atoms that will be deleted once the subsystem is initialized
|
||||
var/list/queued_deletions = list()
|
||||
|
||||
var/init_start_time
|
||||
|
||||
#ifdef PROFILE_MAPLOAD_INIT_ATOM
|
||||
var/list/mapload_init_times = list()
|
||||
#endif
|
||||
@@ -29,6 +31,7 @@ SUBSYSTEM_DEF(atoms)
|
||||
initialized = INITIALIZATION_INSSATOMS
|
||||
|
||||
/datum/controller/subsystem/atoms/Initialize()
|
||||
init_start_time = world.time
|
||||
setupGenetics() //to set the mutations' sequence
|
||||
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
@@ -122,8 +125,11 @@ SUBSYSTEM_DEF(atoms)
|
||||
/// Init this specific atom
|
||||
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, from_template = FALSE, list/arguments)
|
||||
var/the_type = A.type
|
||||
|
||||
if(QDELING(A))
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE
|
||||
// Check init_start_time to not worry about atoms created before the atoms SS that are cleaned up before this
|
||||
if (A.gc_destroyed > init_start_time)
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE
|
||||
return TRUE
|
||||
|
||||
// This is handled and battle tested by dreamchecker. Limit to UNIT_TESTS just in case that ever fails.
|
||||
|
||||
@@ -139,6 +139,13 @@ SUBSYSTEM_DEF(garbage)
|
||||
pass_counts[i] = 0
|
||||
fail_counts[i] = 0
|
||||
|
||||
#ifdef EXPERIMENT_515_QDEL_HARD_REFERENCE
|
||||
// 1 from the hard reference in the queue, and 1 from the variable used before this
|
||||
#define IS_DELETED(datum, _) (refcount(##datum) == 2)
|
||||
#else
|
||||
#define IS_DELETED(datum, gcd_at_time) (isnull(##datum) || ##datum.gc_destroyed != gcd_at_time)
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_FILTER)
|
||||
if (level == GC_QUEUE_FILTER)
|
||||
delslasttick = 0
|
||||
@@ -159,26 +166,32 @@ SUBSYSTEM_DEF(garbage)
|
||||
//Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun.
|
||||
for (var/i in 1 to length(queue))
|
||||
var/list/L = queue[i]
|
||||
if (length(L) < 2)
|
||||
if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT)
|
||||
count++
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
var/GCd_at_time = L[1]
|
||||
if(GCd_at_time > cut_off_time)
|
||||
var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME]
|
||||
var/GCd_at_time = L[GC_QUEUE_ITEM_GCD_DESTROYED]
|
||||
if(queued_at_time > cut_off_time)
|
||||
break // Everything else is newer, skip them
|
||||
count++
|
||||
var/refID = L[2]
|
||||
|
||||
#ifdef EXPERIMENT_515_QDEL_HARD_REFERENCE
|
||||
var/datum/D = L[GC_QUEUE_ITEM_REF]
|
||||
#else
|
||||
var/refID = L[GC_QUEUE_ITEM_REF]
|
||||
var/datum/D
|
||||
D = locate(refID)
|
||||
#endif
|
||||
|
||||
if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
if (IS_DELETED(D, GCd_at_time)) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
++gcedlasttick
|
||||
++totalgcs
|
||||
pass_counts[level]++
|
||||
#ifdef REFERENCE_TRACKING
|
||||
reference_find_on_fail -= refID //It's deleted we don't care anymore.
|
||||
reference_find_on_fail -= text_ref(D) //It's deleted we don't care anymore.
|
||||
#endif
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
@@ -194,7 +207,7 @@ SUBSYSTEM_DEF(garbage)
|
||||
switch (level)
|
||||
if (GC_QUEUE_CHECK)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(reference_find_on_fail[refID])
|
||||
if(reference_find_on_fail[text_ref(D)])
|
||||
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references))
|
||||
ref_searching = TRUE
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
@@ -202,12 +215,17 @@ SUBSYSTEM_DEF(garbage)
|
||||
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references))
|
||||
ref_searching = TRUE
|
||||
#endif
|
||||
reference_find_on_fail -= refID
|
||||
reference_find_on_fail -= text_ref(D)
|
||||
#endif
|
||||
var/type = D.type
|
||||
var/datum/qdel_item/I = items[type]
|
||||
|
||||
log_world("## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --")
|
||||
var/message = "## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --"
|
||||
#if DM_VERSION >= 515
|
||||
message = "[message] (ref count of [refcount(D)])"
|
||||
#endif
|
||||
log_world(message)
|
||||
|
||||
#ifdef TESTING
|
||||
for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage
|
||||
var/client/admin = c
|
||||
@@ -242,19 +260,27 @@ SUBSYSTEM_DEF(garbage)
|
||||
queue.Cut(1,count+1)
|
||||
count = 0
|
||||
|
||||
#undef IS_DELETED
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_FILTER)
|
||||
if (isnull(D))
|
||||
return
|
||||
if (level > GC_QUEUE_COUNT)
|
||||
HardDelete(D)
|
||||
return
|
||||
var/gctime = world.time
|
||||
var/refid = text_ref(D)
|
||||
var/queue_time = world.time
|
||||
|
||||
D.gc_destroyed = gctime
|
||||
#ifdef EXPERIMENT_515_QDEL_HARD_REFERENCE
|
||||
var/refid = D
|
||||
#else
|
||||
var/refid = text_ref(D)
|
||||
#endif
|
||||
if (D.gc_destroyed <= 0)
|
||||
D.gc_destroyed = queue_time
|
||||
|
||||
var/list/queue = queues[level]
|
||||
|
||||
queue[++queue.len] = list(gctime, refid) // not += for byond reasons
|
||||
queue[++queue.len] = list(queue_time, refid, D.gc_destroyed) // not += for byond reasons
|
||||
|
||||
//this is mainly to separate things profile wise.
|
||||
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
|
||||
|
||||
Reference in New Issue
Block a user