Files
Bubberstation/code/datums/elements/atmos_sensitive.dm
LemonInTheDark 74892ae7ec Optimization pass focused on foam code (saves about 30% of cpu usage I think) (#76104)
## About The Pull Request

Foam is crummy at high load rn, both because it runs on a low priority
background subsystem, and because it wastes a bit of time.
Let's reduce usage (while speeding up a bunch of other stuff too), and
give it more cpu generally.

[Optimizes reagent processing
somewhat](d409bd4afc)

Turns out most of the cost of foam is the reagents it carries, and the
varying effects they have
I'm doing my best here to optimize them without touching "user space"
too much

That means doing things like prechecking if we're gonna spawn on top of
an existing decal (from glitter, flour, etc), and using that same proc
to also avoid spawning on unacceptable turfs (I had to convert
inheritance to a bitflag system to make this work, but I think that's ok
since we want it imparative anyhow)

It's actually nice for code quality too, since it lets me clean up code
that was using raw locates and weird var pong.
god I wish I had implied types man

[Optimizes foam spreading in its most accursed aspect, reagent
copying](5cc56a64ad)

Holy shit reagent code is a lot.

I'm doing a bunch of small things here. istype in init -> typecache,
removing procs that are called once and loop over a list we JUST looped
over (ph and the caching for reactions in particular)

I am mainly trying to optimize copy_to here, since that's what foam
spams
As a part of this, I removed a pair of update_total and handle_reactions
calls that were done on the reagents we are copying FROM

I have no god damn idea why you would want to do that, but if anything
is relying on the copy proc modifying the source, then that code
deserves to break

Speaking of, I cleaned up handle_reaction's main filter loop a lot,
removed a lot of redundant vars and changed it from a full loop w
tracker vars to an early exit pattern

This meant using a loop label, which is unfortunate, but this is the
fastest method, and it does end up cleaning up the code significantly,
Which is nice

Oh also I made the required_other var function even if there is no atom
attached to the reaction, since I don't see why it wouldn't

This last bit is gonna get a bit esoteric so bear with me

Failing calls (which are most of them) to handle_reactions are going to
be fastest if they need to check as few reactions as possible

One reagent in a reaction's required list is marked as the "primary",
and thus gets to trigger checking it.
We need all the reagents to react anyhow, so we might as well only check
if we have one particular one to avoid double checking

Anyhow, in order to make most calls the fastest, we want these reactions
distributed as evenly as possible across all our reagents.
The current way of doing this is just taking the first reagent in the
requirements list and using it, which is not ideal

Instead of that, lets figure out how many reactions each reagent is in,
then divy reactions up based off that and the currently divvied
reactions

This doubles the reagent index count, and takes the most common reagent,
water, from 67 reactions to I think like 22

Does some other general cleaning in reagent code too, etc etc etc

[Fixes runtimes from the forced gravity element being applied more then
once](941d067611)

I feel like this element should take a trait source or something to make
them potentially unique, it's too easy to accidentally override one with
another

[Removes connect_loc usage in atmos_sensitive, replaces it with direct
reg/unreg](de1c76029d)

I only really used it because I liked the componentization, but it costs
like 0.2 seconds off init alone which is really stupid, so let's just do
this the very slightly harder way

[Micros foam code slightly by inlining a LinkBlockedWithAccess
call](744da3694c)

This is in the space of like 0.05 seconds kinda save so I can put it
back if you'd like, the double loop just felt silly

[Changes how foam processes
slightly](ee5e633e32)

Rather then treating spreading and processing as separate actions, we do
both in sync.
This makes foam fade faster when spreading, which is good cause the
whole spread but unclearing foam thing looks silly.
It also avoids the potential bad ending of foam spreading into itself,
backwards and forwards. This is better I promise.

[Bumps fluid priority closer to heavy eaters, moves it off
background](811797f09d)

Also fixes a bug where foam would travel under public access airlocks.

## Why It's Good For The Game

Saves a lot of cpu just in general, from both init and live.
In theory makes foam faster, tho I'd have to test that on live at
highpop to see if I've actually succeeded or not. Guess we'll see.
2023-07-17 08:56:24 -07:00

86 lines
3.0 KiB
Plaintext

//This element facilitates reaction to atmos changes when a tile is inactive.
//It adds the object to a list on SSair to be processed for so long as the object wants to be processed
//And removes it as soon as the object is no longer interested
//Don't put it on things that tend to clump into one spot, you will cause lag spikes.
/datum/element/atmos_sensitive
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
/datum/element/atmos_sensitive/Attach(datum/target, mapload)
if(!isatom(target)) //How
return ELEMENT_INCOMPATIBLE
var/atom/to_track = target
if(to_track.loc)
to_track.RegisterSignal(to_track.loc, COMSIG_TURF_EXPOSE, TYPE_PROC_REF(/atom, check_atmos_process))
RegisterSignal(to_track, COMSIG_MOVABLE_MOVED, PROC_REF(react_to_move))
if(!mapload && isopenturf(to_track.loc))
to_track.atmos_conditions_changed() //Make sure you're properly registered
return ..()
/datum/element/atmos_sensitive/Detach(atom/source)
if(source.loc)
UnregisterSignal(source.loc, COMSIG_TURF_EXPOSE)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
if(source.flags_1 & ATMOS_IS_PROCESSING_1)
source.atmos_end()
SSair.atom_process -= source
source.flags_1 &= ~ATMOS_IS_PROCESSING_1
return ..()
/datum/element/atmos_sensitive/proc/react_to_move(atom/source, atom/movable/oldloc, direction, forced)
SIGNAL_HANDLER
if(oldloc)
source.UnregisterSignal(oldloc, COMSIG_TURF_EXPOSE)
if(source.loc)
source.RegisterSignal(source.loc, COMSIG_TURF_EXPOSE, TYPE_PROC_REF(/atom, check_atmos_process))
source.atmos_conditions_changed() //Make sure you're properly registered
/atom/proc/check_atmos_process(datum/source, datum/gas_mixture/air, exposed_temperature)
SIGNAL_HANDLER
if(should_atmos_process(air, exposed_temperature))
if(flags_1 & ATMOS_IS_PROCESSING_1)
return
SSair.atom_process += src
flags_1 |= ATMOS_IS_PROCESSING_1
else if(flags_1 & ATMOS_IS_PROCESSING_1)
atmos_end()
SSair.atom_process -= src
flags_1 &= ~ATMOS_IS_PROCESSING_1
/atom/proc/process_exposure()
var/turf/open/spot = loc
if(!isopenturf(loc))
//If you end up in a locker or a wall reconsider your life decisions
atmos_end()
SSair.atom_process -= src
flags_1 &= ~ATMOS_IS_PROCESSING_1
return
if(!should_atmos_process(spot.air, spot.air.temperature)) //Things can change without a tile becoming active
atmos_end()
SSair.atom_process -= src
flags_1 &= ~ATMOS_IS_PROCESSING_1
return
atmos_expose(spot.air, spot.air.temperature)
/turf/open/process_exposure()
if(!should_atmos_process(air, air.temperature))
atmos_end()
SSair.atom_process -= src
flags_1 &= ~ATMOS_IS_PROCESSING_1
return
atmos_expose(air, air.temperature)
///We use this proc to check if we should start processing an item, or continue processing it. Returns true/false as expected
/atom/proc/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return FALSE
///This is your process() proc
/atom/proc/atmos_expose(datum/gas_mixture/air, exposed_temperature)
return
///What to do when our requirements are no longer met
/atom/proc/atmos_end()
return