When I look at my character’s scripts I feel like I have so many scripts that cache the same components in each script and I wonder if this actually has any impact on performance.
Consider this scenario:
I have 100 different scripts on a gameobject that all do something different but they all require a variable from script A.
So in each of those 100 scripts I have to do a GetComponent<ScriptA>().variable.
So let’s say I have to use it alot and I want to cache it, so I create a variable for scriptA and do a
ScriptA scriptA;
void Awake(){
scriptA = getcomponent<ScriptA >();
}
in each of those 100 scripts.
Is there any downside in doing this or is there a better way?
Does this make the loading of the game slower cause there are so many getcomponent calls in awake for example?
Would it be better to just call the getcomponent instead of caching it (when used outside of update)?
The only way to assess performance is to attach the profiler.
Conventional Unity3D wisdom holds that grabbing all those references in Start() instead of as you’re going along is more performant, assuming they are invariant for the lifetime you know them to be.
This seems intuitive, but I have never seen evidence or an experiment done that would prove or disprove it.
Again though, if you’re worried about performance when you are NOT having performance issues, that’s not what I would consider a good use of engineering time, but your time is of course your own to spend.
Often I don’t do GetComponent caching. I just create serializable fields and assign them in the inspector. No GetComponent calls at all that way. And no code changes if a component needs to be moved to a different object.
That being said… having 100 scripts on a single object means you’re probably doing something else in a less than optimal way.
The other thing to consider is what does ScriptA do? Is it a manager script and that is why all the other scripts need a reference to it? Does it just hold a bunch of data? Depending on it’s purpose, you might just need a singleton or a manager script that can be called easily by other scripts without having to connect a reference.
Yeah this was also an idea I had but the issue stays more or less the same.
This way I have to assign the same couple of components to those 100 scripts manually which is just tedious and that would make getcomponent alot faster.
Or you could make a specific script that caches each component and then reference that “cachescript” in each other script
100 scripts is exaggerated ofcourse it was just hypothetical but the reason I use 100 as an example is because I’m using a component based design which could cause a gameobject to have 20 components or so.
So 20 components, 5 getcomponents in each script, for 20 gameobjects for example is is already 2000 getcomponent calls in a possible single frame.
Ye exactly.
For example playercontroller contains the player’s state or something.
Several components on that character need to know that state for some reason.
Do that a couple more times for the audiosource, collider, health, etc… for several characters.
It starts to add up.
I gotta quibble there sir Praetor… GetComponent is indeed being called, it’s just being done before you get called in Awake()/OnEnable(), that’s all.
I take your point that you don’t have to maintain those GetComponent calls, but it just changes the boundary of who is responsible for doing the GetComponent… and we all know that if those GUIDs ever change, that component reference will be null just as sure as if you forgot to add GetComponent yourself.
This is why I’m asking, I do have an issue
Aside from trying to speed up loading times I have a problem where my build freezes up when loading a scene async with addressables.
Weird thing is the game loads perfectly when I launch for the first time but the times after it freezes and becomes unresponsive.
I’m trying to troubleshoot it cause I have no idea why it freezes. I suspect something with memory or the scene/scripts being too “busy” hence my question.
Interesting. And I take it you’ve been unable to profile it at the moment it hurks on the second launch?? Are you seeing it on the built target or only in editor?
It won’t be an improvement to move something that you’re doing too much of in Start() and instead do 60x per second more of it in Update()!
Not sure how much RAM you’re using but it could be some kind of garbage collection event… but really the only truth is the profiler, if you can wrestle it to give you that information. If you cannot, you sorta have to go with elimination of steps to find it, which is …less than ideal.
But I’ve had that happen before where profile-built development mode targets simply refused to run on the low-end hardware, which (of course) was the only place we had the problem.
“Okay, remove audio.” - Nope still problem
“Okay, force all textures to 64x64 pixels” - Nope still there (this techique will quickly reduce your RAM footprint from textures, and it may get your profiler build running on hardware that it couldn’t run on before… handy!)
I also created an attribute that through my custom editor system it auto assigns the value from itself. I call the attribute ‘DefaultFromSelf’.
I like this approach of having it assigned through inspector because it visually says to me the dependencies a component has when I add it to the GameObject. This way if my designer is adding some new component and it’s not working, he can just look at the inspector and see those "nothing"s in the inspector and resolve the issue on his own.
Not entirely sure whether the injection of serialized component references which is done by the engine is faster than caching by calling GetComponent in Awake or Start.
One thing we all know though is that Awake/Start only runs on the main thread, whereas injecting the component references could potentially be done more efficiently behind the scenes.
So you’d have to profile that and check the start-up times with a huge amount of components, once cache them within Awake, once assign them to serialized fields and compare the results.
Afterwards, re-evaluate whether the number of components you need in your application are somewhat close to the number of components it requires to see a significant difference - if there’s any at all.
If the injection by Unity were faster, you could use OnValidate to auto-fill your serialized [component] fields, so that you neither need to drag&drop them into the slots, nor need to call GetComponent in Awake.
*Edit Lord’s attribute would be much cleaner, as you’d just need to annotate your fields and don’t need to maintain OnValidate.
It stops profiling the moment it’s frozen so ye profiler is off no help.
So indeed I’m just playing a guessing game.
I doubt ram is the issue, I have 16 GB and it’s not like I’m loading in a huge battle royale map with 100 players
It’s just a a “small village town” but it does have trees/bushes/water reflection/terrain/alot of small scenery like rocks, etc…
So it’s not empty either.
All of this could also be the perpetrator ofcourse like static batching/occlusion/who knows.
That’s why I started this topic cause it’s just one of my guesses I’m trying too eliminate.
It’s not so much that I’m gonna do everything in update I’m just wondering off the impact of calling too much code in the first couple of frames.
Then I have special thing I call “PropertyModifiers”, that are similar to PropertyDrawers, but instead of drawing the property… it only serves the job of modifying it.
If we wanted to scale this back some since the ‘DefaultFromSelf’ behaviour doesn’t actually need to hook into this system of mine. Instead in SPEditor we could snip out a lot of it.
Still have the sealed ‘OnInspectorGUI’, have the ‘OnSPInspectorGUI’ (or whatever you wanna call it) that calls in the middle of ‘OnInspectorGUI’. And then before calling ‘OnSPInspectorGUI’ you just loop through all the fields, pull attributes off of them, and filter out any that do things like DefaultFromSelf, and then perform what is necessary.
Mind you again, a LOT is going on in here because my system supports a lot of tools. Like the ‘VariantReference’ that is referenced in there. But you can snip all that out and boil it down to the specifics which is the ‘else’ after testing for VariantReference.
…
But having my multipropertyattributeblahblah allows me to accent my scripts without having to write custom editors all the time. Like so:
Can give me:
Which auto makes it use the reorderable array list drawer, it restricts what I can drop there to the interface to things that implement IOccupiedTrigger, and when I press play I can’t interact with the property.
I’d argue the visuals is where I’d start first. If a scene is chugging, GetComponent is low on my list. Visuals/Assets are high as rendering is the biggest beast in the game by far always. I’d follow that with things like Physics, and that with things like if you have a lot of scripts active that all use ‘Update’ (even just having the Update empty in your script costs something).
…
It sucks you’re having issues with the profiler. Cause really that’s the go to in this moment. What do you mean by “It stops profiling the moment it’s frozen so ye profiler is off no help.” ? If it’s profiling up to that point I’d look for what is costing a lot leading up to being “frozen”… kind of like a derivative/limit in calculus… you maybe don’t know what is at the undefined point in f(x), but you can calculate the values heading up to that point in f(x) to guess at what it is.
Are things completely freezing (and you need to alt+F4 out of Unity)? Or just slow? If it’s completely freezing you may have an infinite loop in your code (or a much less likely deadlock).
Slow startup code will only cause… well, a slow and hiccupy startup.
Yanno, I’m sorry I misread your “build freezes up” line… you were very specific but because of the original post I mis-interpreted that as “it just pauses for a bit.”
As Praetor observed above, if you are truly crashing, it won’t be from CPU performance. It is most likely an infinite loop somewhere.
@lordofduct Thanks for the thorough breakdown… appreciate you taking the time. I wish it was as simple as just implementing an attribute and calling it a day!
I don’t know if the performance is different, but if everything needs a variable from Script A, definitely use a static class, which is accessible from any script in any scene (it’s not attached to a GameObject). No need to call and/or cache any components, no need to worry about losing data between scenes, etc. That static class can also hold utility functions.
Ye completely frozen, the exe becomes “not responding”.
I doubt it’s an infinite loop cause it works perfectly in the editor, and even launches the first time after build.
It’s most certainly something with loadscene.async and something memory related.
The exe becomes “not responding” during the scene load and the profiler stops recording the second it becomes unresponsive.