Search Unity

Компонент Unity Messaging позволяет вам описывать «волшебные» методы, которые вызываются во время игры при определенных условиях. Эта функциональность понятна и проста в применении, в том числе и для новых пользователей!

Например, если описать метод Update(), как показано ниже, он будет вызываться каждый раз при смене кадра

Этот метод может показаться странным опытному разработчику по трем причинам:

  1. Неясно, как именно вызывается метод.
  2. Неясно, в каком порядке вызываются методы, если сцена содержит несколько объектов.
  3. Стиль кода не работает с intellisense.

Как вызывается update() ?

Unity не использует System.Reflection для поиска «волшебных» методов.

При первом обращении к определенному типу MonoBehaviour происходит инспекция соответствующего ему скрипта с помощью Mono или IL2CPP. Собирается и кэшируется информация об описываемых в MonoBehaviour «волшебных» методах. Например, если описан метод Update(), то скрипт, содержащий описание, добавляется в список скриптов, которые обновляются при каждой смене кадра.

Пока идет игра, Unity просматривает кэшированные списки и вызывает найденные методы. При этом не имеет значения, какой модификатор доступа используется для Update.

Порядок вызова

Порядок определяется настройками Script Execution Order Settings (Edit > Project Settings > Script Execution Order). В этом меню можно выстроить порядок вручную — хотя это, конечно, неудобно при наличии большого количества скриптов. В будущем мы рассмотрим более удобные методы определения порядка — например, с помощью свойств внутри кода.

Проблемы с Intellisense

Большинство интегрированных сред разработки, используемых для редактирования скриптов C#, не умеют работать с «волшебными» методами и не могут определить, когда их нужно вызывать. При сборке выдаются предупреждения; в целом, ориентирование в коде затрудняется.

Иногда разработчики добавляют абстрактный класс, расширяющий MonoBehaviour, и для каждого скрипта в проекте расширяют новый класс. Обычно в него помещают набор виртуальных «волшебных» методов. Например, таким образом (класс назван «BaseMonoBehaviour»):

В таком случае использование MonoBehaviour в коде C# становится более логичным. Однако у этого метода есть проблема.

Все MonoBehaviour попадают во внутренние списки Unity, и все эти методы будут вызываться для каждого скрипта каждый раз при смене кадра, подавляющее число раз — вхолостую.

На первый взгляд это несерьезно, так как вызываться вхолостую будут пустые методы. Проблема в том, что вызовы будут производиться из нативной среды C++ в среду C#, что создаст дополнительную нагрузку.

Вызываем Updates 10000 раз

Для этого поста я создал небольшой пример, который выложен на Github. Он содержит две сцены, переключение между которыми осуществляется нажатием клавиши:

(1) В первой сцене создается 10 тыс. MonoBehaviour, содержащих следующий код:

(2) Во второй сцене создается столько же MonoBehaviour, но в них используется пользовательский метод UpdateMe, который вызывается управляющим скриптом при каждой смене кадра:

В качестве теста мы запускали проект на двух устройствах iOS после сборки в Mono и IL2CPP в режиме Non-Development и конфигурации Release. Мы использовали таймер для оценки производительности таким образом:

  1. Таймер включался при первом вызове Update (с помощью Script Execution Order).
  2. Таймер останавливался при вызове LateUpdate.
  3. После нескольких минут работы высчитывалось среднее время.

В тесте использовались следующие версии ПО: Unity 5.2.2f1, iOS 9.0

Mono

WOW! Это много! Должно быть, с этим тестом что-то не так!

На самом деле, я просто забыл установить Script Call Optimization в значение Fast but no Exceptions. И теперь мы видим, какое влияние на производительность оказывает это значение… о котором многие в IL2CPP не беспокоятся.

Mono (fast but no exceptions)

OK, это значительно лучше. Давайте переключимся в IL2CPP.

IL2CPP

Здесь мы видим две вещи:

  1. Эта оптимизация все еще имеет значение в IL2CPP.
  2. В IL2CPP по-прежнему есть то, что требует улучшения. И пока я пишу этот пост, команды Scripting и IL2CPP работают над повышением производительности. Например, новейшая ветка Scripting включает оптимизацию, благодаря которой тесты проходят на 35% быстрее.

Я расскажу, что происходит у Unity «под капотом» ниже. Сейчас давайте изменим наш код Manager, чтобы он работал в 5 раз быстрее!

Вызовы интерфейса, виртуальные визовы и доступ к массивам

Оказывается, что для повышения скорости перебора нашего списка из 10000 элементов лучше заменить List<> на обычный массив, так как сгенерированный код С++ работает быстрее. При замене List<ManagedUpdateBehavior> на ManagedUpdateBehavior[] действительно происходит заметное ускорение.

Выглядит значительно лучше!

Апдейт: Я запускал этот тест с массивом в Mono и получил 0.23мс

Инструменты спасения!

Мы выяснили, что вызов функций из C++ в C# не очень быстрый, но давайте посмотрим, что именно происходит, когда Unity вызывает Update по отношению к различным объектам. Для этого мы воспользовались компонентом Time Profiler из набора Apple Instruments

Обратите внимание, что это не тест Mono vs. IL2CPP test — большинство вещей, которые будут описаны ниже, верны и для билдов Mono iOS.

Для исследования использовался iPhone 6. Тест с Time Profiler был запущен на несколько минут, после чего был рассмотрен минутный интервал, начинающийся с этой строки:
void BaseBehaviourManager::CommonUpdate<BehaviourManager>()

В правой колонке представлены функции, отсортированные по времени исполнения, и вызываемые ими другие функции. Левая колонка — общая занятость процессора по времени (мс) и по процентам для каждой функции и всех вызываемых ею функций. Средняя колонка — время исполнения функции без учета вызываемых ею функций.

Так как Unity не загружал систему полностью, вызовы Update происходили только 10 секунд из 60. Рассмотрим поближе те функции, которые заняли больше всего времени.

UpdateBehavior.Update()

В центре виден сам метод Update: IL2CPP называет его UpdateBehavior_Update_m18. До того, как происходит вызов Update, Unity проходит достаточно долгий путь.

Перебор Behaviour

Unity выполняет обновления для всех Behaviour. Класс-итератор SafeIterator перебирает все зарегистрированные объекты этого типа (что занимает 1517 мс из 9979), чтобы исключить ошибку — например, в том случае, если следующий объект в списке окажется удален.

Проверка корректности вызова

Unity проверяет, относится ли каждый производимый вызов к существующему методу активного GameObject, который был инициализирован и чей метод Start был вызван ранее. Это занимает 2188 мс из 9979.

Подготовка к вызову метода

Unity создает ScriptingInvocationNoArgs (вызов из нативной среды к управляемой среде) и ScriptingArguments, после чего дает команду виртуальной машине IL2CPP на задействование метода (функция scripting_method_invoke). Это занимает 2061 мс из 9979.

Вызов метода

Функция scripting_method_invoke проверяет правильность передаваемых аргументов (900 мс), после чего вызывает метод Runtime::Invoke виртуальной машины IL2CPP (1520 мс). Runtime::Invoke сначала проверяет, существует ли вызываемый метод (1018 мс), а потом вызывает сгенерированную функцию RuntimeInvoker для сигнатуры метода (283 мс). RuntimeInvoker вызывает функцию Update (42 мс).

И прекрасная цветная табличка.

Управляемые обновления

Видно, что большую часть времени исполнения занимает функция IL2CPP ManagedUpdateBehavior_UpdateMe_m14; также вызываются те же методы, что и в предыдущем тесте, хотя некоторые из них не представлены в таблице из-за того, что занимают менее 1 мс. IL2CPP также проводит проверку на NULL по отношению к перебираемому массиву.

На изображении ниже используются те же цвета.

В целом, исполнение происходит гораздо быстрее.

Несколько слов о тесте

Если быть честным, этот тест не полностью корректен. Unity использует множество проверок, чтобы обеспечить правильное функционирование игры. В отличие от написанного вручную скрипта управления обновлениями из второй сцены, Unity каждый раз проверяет наличие и активность GameObject, наличие нужного метода, последовательность действий MonoBehaviour и т.д.

В реальной ситуации управляющий скрипт был бы гораздо сложнее, и его исполнение проходило бы медленнее. Однако стоит заметить, что разработчик игры при создании такого скрипта обычно действует, исходя из знания внутреннего устройства игры: ему известно, как именно ведут себя написанные им объекты и какие проверки следует проводить. Unity не имеет такой возможности

Как лучше поступить?

Конечно, правильная последовательность действий при разработке зависит от самого проекта. Однако достаточно часто в играх используется большое количество GameObject в одной сцене, и каждый из них выполняет некоторые действия при каждой смене кадра. Даже если каждый объект исполняет лишь небольшой фрагмент кода, вычислительные затраты на тысячи вызовов Update при большом количестве объектов могут оказаться неприемлемыми — и в таком случае придется переделывать архитектуру игры под использование управляемых обновлений и проводить рефакторинг всех объектов.

Теперь у вас есть вся нужная информация, думайте об этом в самом начале вашего следующего проекта.

Комментарии закрыты.

  1. Hey i don’t understand this managed update concept.

    If i have 50 gameobjects with a script component that has update() function and inside the update it accesses gameobject position for example.

    In this case the gameobject position is unique to each gameobject.

    I need to have 50 update() calls, 1 for each gameobject.

    How do I implement an approach where i have only 1 update() in some manager and can access gameobject position of all objects??? i don’t understand how i would do this properly? Would i have to keep all 50 gameobjects in an array and access their positions in a loop and then pass data back into the gameobject?

    Is this better than having 50 update() funtions?

    1. I think you are mixing two tasks together:
      1. A manager script which has an array of your script components on gameobjects which calls UpdateMe() function on each component every frame (as it was done in the example from the post).
      2. A manager script which has an array of gameobjects which does some logic (involving transforms) on all gameobjects in turn.
      The difference is that in (1) each script component does the work and accesses its own transform, while in (2) the manager does something with each transform it gets from array. In both cases every transform belongs to individual gameobject and is unique.

  2. Why not implement an in-house callback manager, that’s what a game engine is supposed to do!

  3. As an old-style developer I highly prefer to have my own handled managers, and only have one Update() on the top-level manager, that I then cascade down to the other classes as needed, only when needed. This blog post proves that this is a sane choice to make, and not implement Update() in each and every class, and have guard-clauses to return for every case when we don’t want it to actually run for that instance etc.

    1. This ^^^

      I’ve been building games this way for something around 20 years. It’s the correct approach for any game, not just unity games. This whole ‘attach every script to an object and use its Update()’ method really strikes me as the wrong way to do things.

  4. Just please make sure to provide plenty of tutorials and update the old ones accordingly to demonstrate.
    I promise you there are plenty of newer «developers» like myself that haven’t made it that far into UNITY yet that we wont have to re-train ourselves to this new approach.
    I don’t have a horse in this race yet so it doesn’t effect my interests yet.

    However I am all for improving Unity and making it better.

    Re-emphasizing — update old tutorials (re do them, please take the time to do this without simply adding annotations as Unity progresses (it only helps old tutorials so much before it isn’t anything like the original version) / this wont scare off potential new developers when something breaks from an old tutorial.

    I may be putting too much stock into what changes / approach the scripting team is doing though.

  5. So, after this article… Is Unity finally going to change the default Script template that ships to remove the empty Start() and Update() functions? Please?

    (I know that the templates can be changed, but srsly… why?)

    1. Still useful for newcomers ;)

      1. I agree, it really helped me when I started with Unity because there are lots of things/functions you still need to know. It doesn’t take that much time to remove them and also, how often do you create a script? Every hour? I doubt it.

        1. Aren’t empty methods typically optimized out by any compiler though?

  6. Has anyone tried this on PC? Is it normal that I’m getting 12ish ms on an i7 4770k, for the Update scene?

  7. Thanks for your article, Valentin! =)
    Going to bookmark it for colleagues who likes to leave empty Start() and Update() methods from the new C# script template even if they don’t need them.

  8. Unity is a very good tool, I don’t know why some people complain. Compared to other tools created in the past decade, Unity is one of the best ones. Go ahead and run Unreal 3, it’s like you’re working with something from the 90’s. Unity has issues, but not that big, plus it’s a good practice for engine developers to follow Unity’s architecture, it worked fine for me.

  9. Interesting that unity posts things like this on their blog.

    The post basically says «the framework is old, bad, and slow, the new framework is even slower, here’s a half finished hack you can use instead».

  10. First of all — great article. I’m always thrilled to learn something new, especially when its something that has a potentially huge impact.

    Second, has this «Managed Update» concept been some sort of Open Secret? This is the first I’ve read about it. All the articles I’ve read about Unity Performance Optimizations did not mention this, and I’ve read quite a few… or did I just miss it? I’ve just tried to search for more articles on the topic but there appears to be very little. I guess better late to the party than never.

    And last, (this is the part where I put on my fireproof pants), does this hold true if we’re developing using Javascript/Unityscript? My guess would be «yes» since for the final device build, I’m using IL2CPP. Just confirming.

    Cheers,
    Manny

    1. Correct. C# and UnityScript are compiled to the same bytecode.
      Also it is worth mentioning that we’d like everyone to use C# instead of UnityScript.

      1. Stop writing documentation examples in Uscript and you’ll see newcomers using c# by default.

      2. Hi Valentin,

        While it look like you will drop support for UnityScript, do you have a plan in the future to support newer version of javascript instead? (es2015 and more) Or you just want a single language used to build a game with Unity?

        Kind Regards.

  11. I try to always use unity ‘tools’ instead of my customized ones … so, I don’t need to face problems because of my «scapes» LOL. Actually, I found messaging system really useful and clear to understand.

  12. «The scripting team are designing a whole new approach»
    Unity team: don’t change anything. Implementing managers in managed code — that’s what many developers already doing. In our case we have whole framework on top of unity api implemented in managed code: for fsm, messaging, etc.
    Unity is already great! Most important — it’s currently free!

    1. Collision events are completely out of our control. So yes, they DO have to fix this

      1. Collision detection already performed by physics engine. You want to perform polling yourself (in managed code)? To avoid undesired collision/trigger events — just disable that object/component when you don’t need it.

        1. I mean collision events also suffer a severe performance loss due to being called by the messaging system. And we can’t make a «manager» for that like we can with Update calls. This is something only unity can fix.

          (Also sorry for accidental double post)

    2. We have no control over OnCollisionStay events, for example. So they DO have to fix this

  13. This is extremely depressing. Depressing enough to make me want to switch to another game engine and tell all my acquaintances to avoid Unity.

    Completely replacing this atrocious messaging system should be Unity’s #1 priority right now, even if it pushes back everything everything else and/or isn’t backwards compatible. Can you at least reassure us that the team is working on a fix?

    1. The scripting team are designing a whole new approach that will (amongst other things) allow for faster scripting invocations, yes.

      1. Can i say something? This is not so bad…think about it…when you profile an application you see it…if you had profile some of your games, you already know that the Update call’s isn’t a bottleneck or something that decreases the performance deeply. It’s just consuming a little bit of time of the proccesor. So, it’s not so chaotical.

      2. Please, if you make such core change make it optional!
        For many people who already work smartly the current version is perfectly fine and the new version will for sure introduce new issues.
        So please if you do make such a big change make it completely separated from the main code like IL2CPP so that it won’t break existing systems.
        I couldn’t stand to wait god know how many version for a stable version because of a feature I don’t need.

      3. I know that folks here wouldn’t agree with me but I would really like to have a C API (not C++ but pure C) exposed, too. We’re using unity but only as «backend» and all the game core (90%) is done in C++ (as a native plugin) using mono embedded API. Another advantage of C API would be one can implement their own scripting subsystem. I don’t/can’t/want to use Unreal 4 because of some missing features/complications.

      4. Please don’t do that. Seriously. Don’t. All you’re going to do is introduce bugs and other issues into games that are already working just perfectly fine with the existing infrustructre just to accomodate a very small minority.

        If you want to do something, redesign Unity’s device input system. That needs a massvie overhaul. How about adding the ability to call gameObject.Find() on objects that are inactive? Lots of things that need improving, but the core architecture of your system works just fine. While I obviously can’t speak for everyone, I personally have no interest in rewriting my whole game to accomodate a massive change that isn’t being asked for.

    2. You can use single MonoBehavior for game logic (not talking about triggers/collision detection). Treat it’s Update() like WinMain(). Manually update your subsystems, like:
      //not a real code, just example
      void Update()
      {
      FSM.update();
      EntityManager.update();

      }

      1. This is what I’m doing. I have 1 monobehaviour script attached to 1 empty game object in the scene. That passes its Update() call to the various loaded managers and tasks the characters have to perform. Only the loaded classes get the calls. So there’s never a problem with 10000 update calls. There’s 1, and it calls the methods it needs to.

        I’m getting outstanding performance even at the highest rendering settings (I never drop below Unity’s capped 60 frames a second).

        There is **zero** reason to completely rewrite the core of Unity. It would very likely do nothing but break many millions of lines of code.

    3. Don’t forget that a lot of game studios made successful games even with these «awful» limitations. Unity is already pretty good as it is, any improvements will just make it better, not «more acceptable».

  14. Thanks a lot for this post since it clarifies a lot of things.

    Having said that, I don’t think the comparison is fair with Mono, for the following reasons:

    1. The version of Mono that Unity is using is rather old. The newest one 4.2.1 has lots of improvements:

    http://www.mono-project.com/docs/about-mono/releases/4.0.0/
    http://www.mono-project.com/docs/about-mono/releases/4.2.1/

    These include, inter ália: better implementations of GC, optimizations in AOT compilation, SIMD and the inclusion of MSFT’s open sourced .NET code.

    2. Any interop call -either from .NET to Native or viceversa- has a penalty, due to, for example, the overhead of mapping unmanaged datatypes to managed ones. So, it’s not a surprise that updating behaviors from within a managed handler turns out to be faster than an interop call between the native part of the engine and behaviors.

    3. C# it-self, as a language, has been and still is improved over time, what in turn means that its methods are periodically reviewed and refactored to increase performance. What is more, the current trend is to go «Universal» with .NET Native so that, marginally, perf diferences between «pure» native code and «converted» one tend to zero. Something similar to what you’re doing with IL2CPP (and as said before, Mono has AOT compilation).

    4. Let me remind you that Unity also has its own version of .NET assemblies (based on C# 3.5) which, compared to .NET’s and Mono’s, could introduce unwanted overhead, specially in comparison to the latest version of the language: C# 6.

    5. UnityEngine was conceived and developed to do interop with other languages than C# (that is, UnityScript and Boo) -I don’t know whether the fact that the first versions of the Unity Editor were meant for MacOSX strongly influenced that decision or not, but its architecture wasn’t tailored to fit C# practices. As a matter of fact, in the attempt of avoiding deep hierarchies in favor of an entity-component-system approach, it does not fully comply with OOP. The fact that you have to query behavior in search of certain operations (like Update) as well as approaches like SendMessage/BroadcastMessage seem like a hack that had to be used as a result of that, which in turn doesn’t bring the best UX out-of-the-box on any IDE.

    6. A corolary of 5 above is how Unity handles events. Not only doesn’t it use C# approach to events but also it avoid the use of the Observable pattern at all. In fact, it goes the other way around: in the editor, for an event like button pressed, you have to specify not only the objects but the operations of those objects to call, instead of just registering as an observer and sending the same customized event args to all observers when the event happens. The result? A cumbersome way of handling events on the editor, in particular, when classes and operations change during game development, and finally,

    7. Lookups on arrays and lists shouldn’t differ in a way that using lists over arrays lead to overheads when traversing lists since the only moment when such penalties should exist is when adding elements obliges the list to expand its inner array. If they differ, then you should check up the version of the Mono compiler you are using, since lookups are optimized in .NET for lists to avoid such thing (and I guess that also in current versions of Mono).

    Again, this post is valuable since it help us deal with perf with Unity, but it also exposes some issues that could be considered design flaws. I don’t want to offend anyone with this. On the contrary, I want it to become a better engine. In this sense, imvho, performance of the UnityEngine not only could be improved with native compilation (IL2CPP) but also by a full redesign so that on the behaviors side it fully complies with OOP and C# standards avoiding interop calls as much as possible. In short: a) get rid of SendMessage approach, b) implement a managed handlers for calls to Update, LateUpdate, and so on so forth, c) either include Update (and other) methods where appropriate in Monobehaviors or create interfaces for them, d) re-implement how you handle events, e) support c# 6, f) upgrade your Mono backend to 4.x, and g) try not to use you own versions of .NET assemblies unless really necessary and optimized.

    I hope you guys consider/are considering this. Thanks again.

  15. Valentin I know back at one of the Unite roadmap sessions this year they had mentioned that there was work being started on a MonoBehaviour replacement/alternative where they might move away from magic methods and make some new choices, do you think that we will here more about it in the near future?

    1. Sure, as Richard said one comment above the scripting team are designing a whole new approach to MonoBehaviours. Expect to hear about it soon.

  16. OK, i read about this somewhere else, so in my current project instead of using Update() as the main loop for my characters i start coroutine in Start(), the coroutine stops when the characters is dead.
    should this improve performance?

    1. I took a look at StartCoroutine, it’s actually a call into the native code. That means it’s handled on the unmanaged side so it should be about the same system as Update, only temporarily. Maybe Overhead is a bit lower, as it does not need to fetch Update from your MonoBehaviour explicitly, but idk. I wouldn’t count on it, but you could actually perform a similar test;)

      1. I updated the test on GitHub and it looks that this Coroutines approach is 5 times slower than using Updates. Logically it should be slower at least because the engine has to make 2 calls to the returned enumerator: moveNext and current.

        1. There’s also the fact that co-routines have a per frame GC alloc, whereas Update does not.

  17. I wouldn’t expect this kind of articles on the unity blog. It’s like to admit that the framework is old and inefficient, but instead to do something about it, let you know that there are alternatives. Luckily there are alternatives.

    1. Developers want to make beautiful and successful games, we help them providing the tools and support services so they’d use the tool right. Meanwhile we are working on improving various subsystems of the engine.

    1. Screwed up the formatting on that.

      Of course, in the future we want to have a more convenient way to specify execution order, using an attribute in code for example.

      I made an asset which does just that.

      It would be pretty cool if the attribute they implement can be used on individual methods. So you could have a script Awake before everything else, but Update after.

  18. Nice. These are the types of posts I’d like to see more of.

  19. What happens with the Update methods from built in scripts that we cannot edit (Camera component, Collider component, MeshRenderer, etc) ?

    1. They’re not scripts, so they don’t work like MonoScripts do. Instead, all components of a particular type are added to a ‘manager’ when they’re enabled, and the manager is called directly at the appropriate point in the main game loop; it then does the work for the components in the most efficient way possible (looping through them one at a time, dispatching them to worker threads, or whatever makes sense for the subsystem in question).

      1. And what about other functions that are Mono Behaviour called repeatedly like LateUpdate (We must manage them too?)?

        Thank you Richard.

        1. Yes. Update, LateUpdate, FixedUpdate — these go through the same code path.

  20. Wow! Thanks for the tip!

  21. Unless I’m missing something, it seems that having a manager in managed code that calls all the update functions is significantly more efficient than letting the engine call them all from c++ code.

    In that case, I ask myself, why not create this manager, in managed code, as part of Unity’s engine, and have it always be the one that calls update? It sounds to me like this should be an architecture decision by Unity, that update calls (and other calls that happen a lot) should be handled from a central part of the engine that runs in managed code to start with.

    Is this something you guys in Unity are thinking about?

    1. See the previous reply.

  22. Nice article, although I personally still hadn’t hit this limit myself.

    A couple of questions:

    1. You mention that you cache the method reference and then invoke it from a list; in case of an empty method — can’t you discard the method so it won’t even be considered for execution? (you’re already using mono.cecil for a bunch of internal tools, you could use it to check if the method body is empty).

    2. Did you consider creating a managed-side Update manager that will avoid most of these costs of native-to-managed calls? (or are those checks expensive for other reasons too?)

    3. The title for one of the sections is «INTERFACE CALLS, VIRTUAL CALLS AND ARRAY ACCESS», but nothing is mentioned regarding those topics… how do interface/virtual calls affect the performance ?

    Again, nice post and keep ’em coming.

    1. 1. I suspect that nobody did this because why would you have an empty Update method in the first place?! o.O But this seems to be a good candidate for the next Hack Week.

      2. I don’t know all the details but it’s not that simple to make it easy and fast. The scripting team is designing the whole new approach as we speak.

      3. This was meant to be the reference to http://blogs.unity3d.com/2015/06/03/il2cpp-internals-method-calls/ since list[i] is an interface call.

      1. Regarding #1, I added that in the base ruleset in «Unity Gendarme» (a fork of the fantastic Gendarme analysis tool). I did that was a while ago while between jobs, so the code is probably a bit crap, but apparently relevant to the discussion.
        https://github.com/fderudder/unity-gendarme/tree/master/gendarme/rules/Unity.Rules.Performance

        Funnily enough, empty methods are mode widespread that you might think at first. Like having a «create new script» template with all the methods by default, so you don’t have to look them up.

        One of the projects I worked on had several empty methods, and I regularly check some released Unity games to check that. You’d be surprised.

      2. If you did detect empty functions, you could actually do the virtual methods interface without punishment.

      3. anonymized to prevent reprisals

        Январь 19, 2016 в 11:24 дп

        There might be an empty Update() method because every single Unity-generated script contains Start() and Update() definitions by default; this was actually mentioned previously in the thread.

        Not to be rude, but your question seems nonsensical, condescending, asinine, and a bit obtuse. Unity3D might do well to reconsider having you in a customer-facing position, or at least moderate your comments to make sure you aren’t alienating new developers with your inconsiderate and thoughtless remarks.

  23. Very interesting article. Am i wrong of concluding that if we have a lot of Update calls on MonoBehaviours it is better to use our own implementation of the Update Calls (Iterates trough monobehaviours with simple arrays and call UpdateMe and so on). I am about to check the project that Valentin uploaded but first i have this doubt.

    And in the other hand, i think that this is something that Unity has to solve as fast as possible.

    Thank you and happy holidays.

  24. I realize this post isn’t meant to compare Mono and IL2CPP, but man, it sure does make IL2CPP look bad. It’s supposed to be innately faster than Mono, yet months into its existence it’s 100% slower in this scenario? Calling MonoBehaviour methods isn’t exactly an edge case.

    1. Well, IL2CPP is faster than Mono in a lot of things:
      http://blogs.unity3d.com/2015/01/29/unity-4-6-2-ios-64-bit-support/
      In this particular case it might not be very well optimized yet. I ran the test with array on Mono and got 0.23ms vs. 0.22ms in IL2CPP. But once again, the post is not to compare virtual machines.

  25. Great to see unity taking a look at this — I’ve been reporting the issue of slow callbacks through bugs, feedback site and talking directly to Unity devs.

    The Update calls are bad enough, but everything using SendMessages (OnEnable etc.) are even worse to a point where we had to seriously re-engineer stuff to make streaming not hickup on unity calling OnEnable. Not the code in of OnEnable, them, just _calling_ them. This was for about 2000 objects on PC level hardware, and ~10 ms was spent on calling those. Yikes.

    The last paragraph slighty concers me. Are we really expected to avoid using Unity callbacks for performance? 10K is not a lot, 2k OnEnable’s is nothing. We can do some special case handling but it feels like it really shouldn’t be a concern to use these callbacks.

    It feels like Unity really needs to take a hard look on how to improve the performance on these callbacks. Looking at the numbers there’s a lot of stuff in there that seems duplicate (checking if the call is valid, then if it exists, then if the arguments are valid….), redundant (lots of time spent in argument handling — when there’ll never be arguments to Update) or cachable (whether method exists etc.).

    It’s good to hear that the IL2CPP runtime is being optimized for this case but it seems that generally it needs to be considered how to structure the engine to make this faster.

    And good to see it’s at least on the radar that it’s slow now :)

    1. OnEnable actually uses the same mechanism as Update. Have you profiled the code and witnessed that it was really just calling this method and not awaking other components on your game objects? Physics component, Animator and other components are pretty costly to create, enable and disable.

      > Are we really expected to avoid using Unity callbacks for performance?
      No, you need to think about this limitation when architecting your game and vigorously profile. If this particular issue affects your game you know what to do.

      Also, as I said recent scripting optimizations remove some of the overhead you mentioned.

  26. It turns out that if you’d wanted to iterate through a list of 10000 elements every frame you’d better use an array instead of a List because in this case generated C++ code is simpler and array access is just faster.

    This kind of comment irks me to no end. On the one hand, Unity is saying «hey, you can program your game with C#, which is an easy-to-use, safe, productive and powerful language!». But then it’s like «oh yeah, all those features that make the language so easy and productive, yeah, you shouldn’t use those because they’re slow». It sends a conflicting message and leads to a lot of unnecessary arguments about what ‘good’ code is. At this point everyone would be better off if we could just write our games directly in C++.

    Not only that, but a List in C# is nothing more than a wrapper around an array with automatic array resizing features. If the IL2CPP compiler had a special case to recognize and deal with Lists specifically (which is what a lot of Microsoft’s .NET compiler tools do too), then there’s no reason why iterating over a List should be any slower than iterating over an array.

    1. I’m guessing it’s because indexing a list is technically a member function, which means il2cpp will do a null check before each call, and maybe not properly inlining this call etc.

      However, I agree that it’s really bad that we have to worry about that. Unity mentioned they’d like to special case optimize IL2CPP for unity code. Seems list access should definitely be one of them.

      1. Yes, indexing a List invokes a call to the getter of the Item property, which in turn indexes the _items array field inside the List object. And since the Item property is an implementation of the IList interface, that call is a virtual method call too, bringing with it all sorts of overhead. Null checks don’t even make the difference here; C# being a safe language will also null-check a plain array when indexing it.

        This is precisely why you would want the IL2CPP compiler to know about Lists and create a shortcut for the indexing operator that accesses the internal array directly. From a puristic standpoint, it’s not nice to give a compiler knowledge about classes inside the framework, but it does open up a lot of possibilities for optimization. And the .NET compiler does it too, e.g. by translating large switch statements into a Dictionary-based lookup table.

        My bigger issue however is that with all these edge cases, people end up writing C# code for Unity as if it is plain old C, which basically means throwing out everything that makes C# worthwhile as a language in the first place. Writing idiomatic C# code (using LINQ, foreach statements, relying on the GC for memory management, etc) in Unity is considered by many as ‘wrong’, leading to a lot of micro-optimization work, and you kind of end up with the worst of both worlds. It makes you wonder why we’re still using Mono at all.

        1. Nico, I understand your concerns and I am the person usually talking to customers explaining the things you mentioned advising them to «make their code dumber». There’s nothing inherently bad in Lists, Lambdas, LINQ, foreach and other .NET features, but considering mobile devices limitations one have to use the most CPU and GC efficient code they can write. This is how mobile devices and Unity as a platform work right now and developers need to know that.

          Not to mention that to write efficient and error-prone code in C++ you need to have a lot of knowledge and experience as well.

          We are working on improving current situation, on Mono/.NET upgrade, implementing various scripting and IL2CPP optimizations. But scripting is only a small part of the engine, we also have a lot of other really important things to do and unfortunately not enough people. Patience, my friend (8

    2. Another thing is that Lists are just slower or more precisely our Mono compiler generates slower code for them. I ran the test with array on Mono and got 0.23ms (was 0.52ms with List) vs. 0.22ms in IL2CPP.

  27. Instead of using an abstract «BaseMonobehaviour» class which implements all these methods, I recommend creating granular, individual interfaces for the particular behaviours you want your objects to implement. For example:


    public interface IUpdate
    {
    void Update();
    }

    public interface IAwake
    {
    void Awake();
    }

    Any «MonoBehaviour» you create implements only the interfaces you intend for it to do:


    public class MyBehaviour : MonoBehaviour, IUpdate
    {
    public void IUpdate()
    {
    //do something
    }
    }

    Of course at this point, you have compile-time safety to catch those nasty typo’d case sensitive names (Anyone out there spend too much time trying to figure out and fix a «void update()»?)

    Regarding intellisense/tooling, with Visual Studio (I can’t speak for the other IDEs), you can auto-implement the interface to deposit the placeholder method automatically. Furthermore, this also gives you a maintenance benefit in that it becomes trivial to find all objects that are «Updating» or «Awaking» or have «FixedUpdate» (just «Find All References» to the particular interface)

    Finally, if you find you keep implementing the same combination of interfaces, you can always combine them to a common interface:


    public interface IStandardBehaviour : IAwake, IStart, IUpdate, IOnEnable
    {

    }

    public class MyBehaviour : MonoBehaviour, IStandardBehaviour
    {
    //implement all 4 interfaces inherited by IStandardBehaviour
    }

    (Sorry if the code formatting above got botched)

    1. Woops, typo’d my first «MyBehaviour» class implementation’s «Update» method name. It should read:


      public class MyBehaviour : MonoBehaviour, IUpdate
      {
      public void Update()
      {
      //do something
      }
      }

  28. Very interesting. Is there anyway to increase performance for OnCollision events? I have a project with a large number (10000+) of GameObjects that have no Update() but all have OnCollisionEnter() and a performance boost would be amazing.

    1. OnCollisionEnter and its friends are implemented through SendMessage so it should be even worse*. Of course Unity doesn’t try to send these messages to scripts which don’t have such callbacks, also one rarely has 10000 objects colliding with each other EVERY frame.

      When working with 2D and 3D physics you should look at other things which reduce performance like not moving static colliders or 2D colliders inside a Rigidbody2D.

      1. Very interesting article. But regarding physics OnCollision/OnStay — especially stay, needs optimising Unity’s side. It is grotesquely inefficient to the point where we had to change the gameplay and there wasn’t alternatives for it (think physical interactions between characters). We really needed stay but it crapped out with just 30 characters on an i7.

        So going forward, if you’re using Sendmessage internally for collision, I don’t think that’s very optimal, do you? Perhaps a better pattern can be achieved?

        Thanks for hard work, regardless :)

        1. Hi,

          Can you open a bug with a test scene in it, when we can get to work on it. Also post the bug Id here.

          Regards,
          Morten

      2. «2D colliders inside a Rigidbody2D»

        Can you elaborate on this? Don’t colliders need to have a rigidbody to register collisions?

        1. Right, dynamic 2D colliders must be children of a Rigidbody2D. And if you move objects with 2D colliders make sure that you move the objects with Rigidbody2D components and not colliders inside Ribidbody2D themselves. If you move a collider manually Box2D has to destroy it and create again every time since there’s no way to move colliders (or fixtures as they are called) in Box2D. This will give you a very noticeable overhead in Profiler if done every frame.