Search Unity

Other How does Update() work from a compiler standpoint?

Discussion in 'Scripting' started by Thing201, Oct 9, 2022.

  1. Thing201

    Thing201

    Joined:
    Apr 14, 2020
    Posts:
    17
    I am not asking, how does update() work. I am asking from a software engineering context, how is it possible / what is the term for a function like update().

    Update() is automatically called in each script that inherits from MonoBehaviour, but if it isn't there there are no compiler errors. The closest thing I can think of is polymorphism but that would be assuming that MonoBehaviour is abstract and would require each derived class to implement it via override.

    Anyways I learned some of this stuff during college, but those were always light on examples, or explanations. I would google something like this but I don't really know the right search terms.

    Thanks!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    The term you want is called reflection and it lets you discover identifier names at runtime, including private ones.

    https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection

    I believe Unity employs at least reflection to access private methods, but given how tightly bound the editor is to the class namespace, it's possible other tricks are in use as well.
     
    Bunny83 likes this.
  3. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,060
    From what I've heard with performance optimization it indeed uses reflection for this. This is why you should avoid having many empty updates in your project and if you have many objects you should directly call scripts instead of them using their own Update.

    Unity does cache the Updates at start, so it doesn't have to search each frame
     
    Bunny83 likes this.
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    I'm guessing it does even more than that and caches it at the method level ONCE for every class on startup, and then it's just a rapid hash table lookup after that, passing in a different
    this
    each invocation.
     
    Bunny83, JoNax97 and DevDunk like this.
  5. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,203
    When you compile code all of the information on how the code is structured is saved alongside the output of the compiler, and at runtime all of this information can be analyzed allowing you to do everything you would normally do as well as things that would normally be blocked like accessing private data and methods.

    https://en.wikipedia.org/wiki/Reflective_programming
     
    Last edited: Oct 9, 2022
    Bunny83 likes this.
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Try to think of what you've learned about modern programming to something akin to human law, instead of engineering rules. These are not hard physical rules to which object-oriented programs have to comply (most of the time), it's more like an industry-adopted convention to improve accessibility, productivity, product management, and collaboration.

    There are ways to bend these rules and conventions, and what Unity does is a prime example of this, simply because of the way it operates beneath the scripting level. So no you cannot explain any of it with abstraction / encapsulation / inheritance / polymorphism. Unity itself calls this "messaging".

    As explained before, Unity uses reflection to establish a direct call with an instance through methods that are known apriori on Unity side but do not have to be declared. Only, well, this isn't entirely true and Unity DOES NOT USE REFLECTION at all. That would be very slow, but the approach is very similar conceptually. Here's an article explaining it in a more detail.
    Regarding reflection in C# however, you can use it yourself as well because reflective programming is widely supported in C#. Also Unity likes to abuse reflection whenever possible (mostly for the editors), because it's much easier to close the gap between C++ and C# that way. Calls simply drop on your lap instead of having to go through OOP hoops.

    I've recently made a custom enumeration for Unity, that works in the editor as expected, but serializes into strings (unlike built-in C# enum type), and you can observe the amount of reflection code in my solution (this is pretty much the standard approach for this kind of work). Reflection is however relatively slow, and we do not depend on it for the actual game runtime, if we can help it.
     
    DevDunk and mopthrow like this.
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Thanks for the article link orion!

    Unity is, as I've always said, a beast of a game engine.
     
    Bunny83 and orionsyndrome like this.
  8. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,203
    Unity only said they're not using System.Reflection to check for the method every frame. If you just stopped there you might interpret that as they're not using it at all, but the paragraph that follows immediately after is very much reflection.
    Let's assume though that you are correct and that it isn't using it. System.Reflection isn't magic or even doing anything on its own. All it does is provide access to the data that is already being used by the CLR to do its job.

    Another way of access it is to simply interact directly with the CLR which you can do if you set up Mono/.NET as a scripting framework rather than as an application framework. Unity is most likely accessing the data in this manner.
     
    Last edited: Oct 10, 2022
    Bunny83 and mopthrow like this.
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    @Ryiah
    I also said
    The important takeaway is that Unity does this on a level with much more control, i.e. it classifies the objects early without having to actually use reflection any more, even the calls themselves are likely pointer bound.

    I for one care about such internals especially when architectural decisions depend on it. As someone who understands System.Reflection API very well, this information on its own would make me wary of how things operate under the hood.

    With the above article in mind, I am still wary, but of something else entirely.

    Nobody said it does anything on its own. But there are a lot of security implications and safeguards when you're circumventing the paths that are intentionally more optimal.

    It doesn't do this blindly. It is thus very slow.

    Here's one topic about its performance (I'm lazy to look for more, and there are plenty more)
    https://stackoverflow.com/questions/771524/how-slow-is-reflection

    Feel free to browse all the answers, and keep in mind that these users aren't necessarily game developers. On average, reflection, when used lightly and tactfully, isn't that much slower, and you can do a lot with it, however a heavy load is likely to destroy any notion of performance, generate tons of garbage, and ultimately you need to be mindful of the types involved, or the signatures you call, wrap and unwrap arguments, create arrays, and do all kinds of things just to be able to clear the safe guards -- and if you don't do it yourself (in case you're thinking "this guy sounds like he never used reflection before"), reflection compounds will actually do this for you, like for instance, the way Activator API works does exactly what I described.

    This is exactly what Unity (and we) want to avoid, so they avoid it altogether. They "sniff" the "messaging" methods, they classify the objects, then mindlessly call entire batches. And even then, as you can see in that article, there is a lot of stuff going on, the call itself being the cheapest thing, so it can't be truly mindless, and we should keep messaging to a minimum.

    I think that the OPs question is very important for everybody to understand the underlying system better, and that it deserves a highly detailed answer. I'm still not entirely satisfied with it, and if I come across more info, I'll remember to share.
     
    Bunny83 likes this.
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,006
    Right, you can use Delegate.CreateDelegate to actually create a direct delegate in C# to a certain method which would be almost as fast as a direct call. From the C++ side it probably is the same since it has to cross the border between the two systems anyways. Though in the end a method call is nothing but a "call" opcode with a certain target memory location. Calling those methods directly is probably faster than using virtual methods that you can override since virtual methods do have a slight overhead due to virtual dispatch.
     
    orionsyndrome and DevDunk like this.