From affbd54d5c152c78978a1717b0c0f3b239cfebf6 Mon Sep 17 00:00:00 2001 From: Kylerace Date: Wed, 18 May 2022 09:13:59 -0700 Subject: [PATCH] updates documentation for how the oranges_ear mob optimization works (#67056) fixes documentation for how the oranges_ear mob optimization works --- code/__HELPERS/spatial_info.dm | 21 +++++++++++-------- code/controllers/subsystem/spatial_gridmap.dm | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm index 93eeee00b7c..f8e5282f081 100644 --- a/code/__HELPERS/spatial_info.dm +++ b/code/__HELPERS/spatial_info.dm @@ -6,14 +6,15 @@ /** # Oranges Ear * * turns out view() spends a significant portion of its processing time generating lists of contents of viewable turfs which includes EVERYTHING on it visible - * and the turf itself. there is an optimization to view() which makes it only generate lists of a certain atom type - this system takes advantage of that. + * and the turf itself. there is an optimization to view() which makes it only iterate through either /obj or /mob contents, as well as normal list typechecking filters + * * a fuckton of these are generated as part of its SS's init and stored in a list, when requested for a list of movables returned by the spatial grid or by some - * superset of the final output that must be narrowed down by view() one of these gets put on every turf that contains the movables that need filtering + * superset of the final output that must be narrowed down by view(), one of these gets put on every turf that contains the movables that need filtering * and each is given references to the movables they represent. that way you can do for(var/mob/oranges_ear/ear in view(...)) and check what they reference * as opposed to for(var/atom/movable/target in view(...)) and checking if they have the properties you want which leads to much larger lists generated by view() * and also leads to iterating through more movables to filter them. * - * TLDR: iterating through just mobs is much faster than all movables when iterating through view(), this system leverages that to boost speed + * TLDR: iterating through just mobs is much faster than all movables when iterating through view() on average, this system leverages that to boost speed * enough to offset the cost of allocating the mobs * * named because the idea was first made by oranges and i didnt know what else to call it (note that this system was originally made for get_hearers_in_view()) @@ -107,12 +108,14 @@ var/old_luminosity = center_turf.luminosity center_turf.luminosity = 6 //man if only we had an inbuilt dview() - //this is the ENTIRE reason all this shit is worth it due to how view() works and can be optimized - //view() constructs lists of viewed atoms by default and specifying a specific type of atom to look for limits the lists it constructs to those of that - //primitive type and then when the view operation is completed the output is then typechecked to only iterate through objects in view with the same - //typepath. by assigning one /mob/oranges_ear to every turf with hearable atoms on it and giving them references to each one means that: - //1. view() only constructs lists of atoms with the mob primitive type and - //2. the mobs returned by view are fast typechecked to only iterate through /mob/oranges_ear mobs, which guarantees at most one per turf + //this is the ENTIRE reason all this shit is worth it due to how view() and the contents list works and can be optimized + //internally, the contents list is secretly two linked lists, one for /obj's and one for /mob's (/atom/movable counts as /obj here) + //by default, for(var/atom/name in view()) iterates through both the /obj linked list then the /mob linked list of each turf + //but because what we want are only a tiny proportion of all movables, most of the things in the /obj contents list are not what we're looking for + //while every mob can hear. for this case view() has an optimization to only look through 1 of these lists if it can (eg youre only looking for mobs) + //so by representing every hearing contents on a turf with a single /mob/oranges_ear containing references to all of them, we are: + //1. making view() only go through the smallest of the two linked lists per turf, which contains the type we're looking for at the end + //2. typechecking all mobs in the output to only actually return mobs of type /mob/oranges_ear //on a whole this can outperform iterating through all movables in view() by ~2x especially when hearables are a tiny percentage of movables in view for(var/mob/oranges_ear/ear in view(view_radius, center_turf)) . += ear.references diff --git a/code/controllers/subsystem/spatial_gridmap.dm b/code/controllers/subsystem/spatial_gridmap.dm index dc63512bae3..0608140bb7c 100644 --- a/code/controllers/subsystem/spatial_gridmap.dm +++ b/code/controllers/subsystem/spatial_gridmap.dm @@ -86,7 +86,7 @@ SUBSYSTEM_DEF(spatial_grid) var/list/grids_by_z_level = list() ///everything that spawns before us is added to this list until we initialize var/list/waiting_to_add_by_type = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(), SPATIAL_GRID_CONTENTS_TYPE_ATMOS = list()) - ///associative list of the form: movable.spatial_grid_key (string) -> inner list of spatial grid types for that key. + ///associative list of the form: movable.spatial_grid_key (string) -> inner list of spatial grid types for that key. ///inner lists contain contents channel types such as SPATIAL_GRID_CONTENTS_TYPE_HEARING etc. ///we use this to make adding to a cell static cost, and to save on memory var/list/spatial_grid_categories = list()