So I stumbled upon a discussion whether or not GetComponent has any internal caching mechanism
My “opponent” claims that it has, because upon first call, he sees a memory allocation, yet on subsequent calls there is not memory allocation at all. It would be a good proof, yet in my tests I couldn’t see any memory allocation even on the first call. Moreover I devised a test case that in my opinion proves that there is no caching
But I cannot be the only person to ask this question, so I’m asking here whether there is an official (or proven) answer to this dilema?
In case someone is interested in my test case (because maybe I’m drawing wrong conclusions).
GetComponent is O(n) where n is the number of components on the object. I assume that the further the component is in some internal component array, the longer it will take to access it.
So I added 3 components to an object. 1 [EmptyComponent], 2 [AnotherEmptyComponent] and 3 [ThirdEmptyComponent] in this order. Every Update I’m calling GetComponent 1’000’000 times on each of those components. The times for component 3 are significantly longer than times for component 1.
If there was indeed caching, then times would be more or less equal for each of those, but they differ by ~25ms between 1 and 2, and the same ~25ms between 2 and 3.
Probably not. Could vary by implementation target, and certainly could vary going into the future.
You have to do your engineering today based on what the documentation guarantees you today. If the docs don’t say it, you can’t count on it and still say you are doing engineering.
Sounds like you have some additional empirical data to help guide you to your engineering solution.
exactly, this is probably the most correct answer as we simply don’t know and can’t know. Though What we do know that there is no caching on the managed code side since every GetComponent call ends up at a native method. So any potential memory allocation you may see does not comes from managed code.
What we also know is the specific, slightly irritating behaviour, that only happens inside the editor: When you call GetComponent on a component type that does not exist on that gameobject, Unity will create a new fake null object which is returned to indicate that no component was found. As it is explained over here this only happens when testing inside the editor. In a built game GetComponent actually returns “null” in case the component does not exist.
Next thing is there would not be any benefit in caching something on the native side since the component instances are just stored in some sort of list (maybe a C++ vector) and calling GetComponent just has to search through that list. So if your gameobject has 5 - 10 components this search is kinda trivial and not as bad as some people think. Though of course, like always, in your own code when you can avoid searching for something by caching it, you would usually do it.
As it was explained in the blog post, Unity may have implemented some kind of internal caching for the the transform property since every gameobject has one. However such a thing makes no sense for the generic GetComponent which takes a type argument as parameter. So in order to implement some kind of “caching” one would need a dictionary to lookup a certain type. However using a dictionary for 5-10 elements is most likely slower than just searching through the list. That’s another common mistake: O(1) is not always better than O(n). It’s better the more elements you have so it’s all about scaling. “Constant time” does not generally mean “fast”. A dictionary lookup is not as trivial as a direct access of an array element. However constant time means it does not get slower as the number of elements increase. So there’s always some kind of “break even point”. In most cases the number of components is relatively small so any kind of caching would probably just make it slower.