[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:
SkyratBot
2023-01-12 16:16:33 -05:00
committed by GitHub
co-authored by Mothblocks tastyfish Kyle Spier-Swenson
parent d2406cf6bd
commit ef1ad8331a
19 changed files with 178 additions and 23 deletions
+7 -1
View File
@@ -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.
+39 -13
View File
@@ -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)