Search Unity

Unityにはメッセージングシステムと呼ばれる、ゲームの実行中に特定のイベントが発生した時に自動的に呼ばれる魔法のメソッドを定義できる機能が備わっています。これはとてもシンプルかつ簡単なコンセプトなので、特に初心者にはありがたい機能です。たとえば短に下記のようにUpdateメソッドを定義するだけで、自動的に毎フレーム呼び出されます!

しかし、経験豊富な開発者からすれば、このコードはちょっとモヤモヤします。

  1. このメソッドは具体的にどのようにして呼び出されているかが明確でない。
  2. シーンに複数のオブジェクトがあった時、どのような順序でこのメソッドが呼び出されるのかが明確でない。
  3. このコーディングスタイルはインテリセンスが効かない。

Updateはどのように呼ばれているのか

「…System.Reflectionだ!そうだろう!!」…いえ、UnityはSystem.Reflectionを使ってメソッドを調べて毎回呼んでいる訳ではありません。

代わりに、任意の型のMonoBehaviourが初めてその基底のスクリプトにアクセスしたときに、スクリプティングランタイム(MonoもしくはIL2CPP)によって何かのマジックメソッドが定義されているかを調査され、この情報がキャッシュされます。もしMonoBehaviourが特定のメソッドを持っていたら所定のリストに追加されます。例えばスクリプトがUpdateメソッドを持っていたら「毎フレームUpdateを呼ぶべきスクリプトのリスト」に追加されるわけです。

ゲーム中は、Unityは短にこのリストをイテレーションしてメソッドを呼んでいきます – シンプルです。また、これがUpdateメソッドのアクセス権がpublicであろうとprivateであろうと関係ない理由でもあります。

Updateはどのような順番で実行されるのか

実行順はスクリプト実行順設定(Editメニュー > Project Settings > Script Execution Order)で設定されます。1000個のスクリプトの実行順序をマニュアルで設定するのはあまりいい方法とは言えないかもしれませんが、とある1つのスクリプトが他のよりも先に呼ばれてほしい、という程度なら現実的でしょう。もちろん、将来的にはもっと便利なやり方、たとえばアトリビュートで設定出来るようにするとかで実行順序を設定できるようにしたいと考えています。

インテリセンスが効かない

UnityでC#のスクリプトを書く時、私たちは誰もがIDE的な何かを使っています。ほとんどのIDEは呼び出し元がハッキリしないマジックメソッド的なものが好きではありません。しばしばワーニングの原因にもなりますし、コードを追う作業も大変になります。

時にデベロッパーにはMonoBehaviourを拡張する抽象クラスを作り、それにBaseMonoBehaviourとかそういった名前をつけて、プロジェクト内の全てのスクリプトをこのクラスを継承するように書くようにする人がいます。この方法では基本的な機能を仮想メソッドとして下記のように定義します:

この構造はあなたのプロジェクトでMonoBehaviour を使う時によりロジカルな構造を導入しますが、一つ欠点があります。おそらく、すでにお気づきだとは思いますが…

全てのMonoBehaviourがUnityが内部で使用するUpdateリストに入り、これらの全てのメソッドが全てのスクリプトについて各フレームで実行され、そしてほとんどのメソッドでは何もしないのです!

空のメソッドについてなぜ気にする必要しなくちゃならないかって?確かにそう思う人もいるかもしれません。問題はこれらのメソッドはC++の世界からマネージドC#の世界のメソッドを呼び出しているということにあり、そこにはコストがあるのです。では、実際のこのコストを見てみましょう。

Updateを10000回呼ぶ

このブログ記事のためにGithubに小さなサンプルプロジェクトを作成しました。このプロジェクトには2つのシーンがあり、デバイスでタップするかエディターで何かキーを入力すると変わります:

(1) 最初のシーンでは下記のようなコードを持つ10000個のMonoBehaviour クラスが作成されます:

(2) 2つめのシーンでは10000個の MonoBehaviourが作成されますが、Updateを使う代わりに別のメソッドとしてUpdateMeというメソッドを作成し、下記のような要領でマネージャースクリプトからこれを毎フレーム呼び出すようにしています:

このテストプロジェクトはDevelopment Modeを使用せず、Releaseの設定でMonoとIL2CPPにそれぞれコンパイルされ、2つのiOSデバイスで動作させました。時間の計測方法は以下の通りです:

  1. 最初のUpdateが実行された時にStopwatchを起動 (Script Execution Order で設定)、
  2. LateUpdateでStopwatchを停止、
  3. 数分間のあいだ計測を続け、平均を出す。

Unity version: 5.2.2f1
iOS version: 9.0

Mono

ぎゃあ!こいつは長いな!テストになにか問題があったに違いない!

じつは、Script Call OptimizationFast but no Exceptions に変更するのを忘れていました。さて気を取り直して設定をしたので、これがどのくらいのインパクトをパフォーマンスに与える話なのか、IL2CPPならもう忘れてもいいようなことなのか、ということが分かる準備が出来ました。

Mono (fast but no exceptions)

OK、だいぶ良くなりました。IL2CPPではどうでしょうか?

IL2CPP

ここで2つのことが見えてきます:

  1. この最適化方法はIL2CPPでも変わらず有効です。
  2. IL2CPPはまだ改善の余地があるということです。このブログ記事を書いているあいだもスクリプティング&IL2CPPチームはパフォーマンス改善のために奮闘しており、実際最新のスクリプティングブランチではこのテストは35%高速に動作しました。

Unityが中でなにをしているのか、ほどなく説明します。ただし、とりあえず今のところはマネージャーコードを変更して5倍速く動くようにしましょう!

インターフェイス呼び出し、バーチャル呼び出しと配列アクセス

もしまだこの素晴らしいIL2CPP Internalsシリーズのブログ記事を読んでいないのであれば、このブログ記事を読み終わったらすぐに読む事をオススメします!

もしあなたが10000個の要素をもつリストをイテレートしたい場合は、Listの代わりに配列を使う方がよいでしょう。なぜならその方が生成されるC++コードがよりシンプルになり、また配列アクセスの方が単純に高速だからです。

次のテストでわたしは List<ManagedUpdateBehavior>ManagedUpdateBehavior[] に変更しました。

うーん、こっちの方が全然いいですね!

更新情報: 配列を使ったテストをMono上で行った結果は 0.23ms でした。

Instrumentを使って調査する

私たちはC++からC#の関数を呼ぶのは速くないということを発見しましたが、ではUnityはこれらのオブジェクトのUpdateを呼び出すときに実際には何をしているのかということを調べていきましょう。一番簡単な方法はApple InstrumentsのTime Profilerを使うことです。

注:このテストはMonoとIL2CPPの比較テストではありません – 以下に説明されているほとんどのことはMonoのiOSビルドでも当てはまる内容です。

わたしはTime Profilerを有効にしてIPhone 6上でテストを起動し、数分間分のデータを記録してそのうちの1分間を詳しく調べてみました。私たちにとって興味があるポイントは以下の行から始まる全ての部分です:
void BaseBehaviourManager::CommonUpdate<BehaviourManager>()

まだInstrumentsを使ったことがない方のために説明をすると、右側には関数が実行時間順にソートされ、その中から呼ばれている関数へと繋がっています。一番左側のカラムは関数が使用したミリ秒単位のCPU時間と関数(とその関数内で呼んだ別の関数)が占めた時間の占有率(%)です。実験中、CPUがすべてUnityの処理によって占められているわけではなかったのですが、60秒間の期間中、約10秒がUpdateの処理によって占められていました。今回探りたいのは、この部分は一体どういう処理によって占められているのかということです。

わたしは自分に秘められた禁断のPhotoshopスキルを解放して、みなさんにも何が起きているか分かりやすいようにプロファイル結果を色分けしてみました。

UpdateBehavior.Update()

ちょうど中程のところでUpdateメソッド – IL2CPP的にはUpdateBehavior_Update_m18 が呼ばれています。しかし、そこにたどり着く前にUnityは様々なことを行っているのがわかります。

すべてのBehaviorsをイテレートする

UnityはすべてのBehavior インスタンスを訪ねてアップデートを行います。特別なイテレータークラスであるSafeIteratorは、誰かが突然リストに登録されている次のアイテムを削除することにしても問題が起きないよういい感じに処理してくれます。すべてのBehavior インスタンスをイテレートするだけで全処理時間 9979ms のうちの 1517ms を占めています。

呼び出しが有効かどうかをチェックする

次に、Unityは初期化され、かつStartメソッドが呼ばれた後のアクティブなゲームオブジェクトの有効なメソッドを呼び出している、ということを確認するために幾つかのチェック処理を行います。Updateの処理中に破棄したGameObjectのせいでゲームがクラッシュしたりして欲しくないですよね?そういうチェックが全体の9979msのうち、2188msを占めています。

メソッドの呼び出しを準備する

Unityはネイティブコードからマネージドコードの呼び出しを行うためにScriptingArgumentsとともにScriptingInvocationNoArgsインスタンスを作成し、IL2CPPバーチャルマシンに(scripting_method_invoke 関数を通して)メソッドの呼び出しを指示します。このステップは全体の9979msのうち2061msを占めています。

メソッドを呼び出す

scripting_method_invoke関数は渡された引数が有効かをチェックし(900ms)、その上でIL2CPPバーチャルマシンのRuntime::Invokeメソッドを呼びます(1520ms)。まずRuntime::Invokeは渡されたメソッドが存在するかどうかをチェックします(1018ms)。そして、生成されたRuntimeInvoker関数を指定の関数シグネチャについて呼び出します(283ms)。これが最終的にUpdate関数の呼び出しになり、Time Profilerによれば実行には42msかかっています。

下記のカラフルで素敵なテーブルに結果をまとめました。

マネージャーを使ったUpdate

では、Time Profilerを使ってマネージャーのテストをしてみましょう。スクリーンショットから同様のメソッドがあることがわかります。もっとも、いくつかのメソッドは1msに満たないため表示すらされていません。ただし、ほとんどの処理時間はUpdateMe関数(IL2CPP的にはManagedUpdateBehavior_UpdateMe_m14)が占めています。それと、IL2CPPが配列がNULLに対してイテレートしていないことを保証するために挿入したNULLチェックもあります。

次のイメージは同じルールで色付けしています。

さて、どう思いますか?ちょっとしたメソッドの呼び出し時間について気を配るべきだと思いますか?

このテストに関するちょっとした意見

正直なところ、このテストは完全にフェアなテストとは言えません。Unityはあなたとあなたのゲームが意図しない振る舞いやクラッシュにはまり込まないように、とてもいい仕事をしています。このゲームオブジェクトはアクティブ?あれ、今回のUpdateのループ中に破棄しなかったっけ?このオブジェクトにはそもそもUpdateメソッドが存在する?MonoBehaviourインスタンスがこのUpdateの処理中に生成されたどうしたらいい? – わたしのマネージャースクリプトはこういう問題については一切の対応をしておらず、単純にオブジェクトのリストをイテレートしてアップデートのための処理を呼んでいるだけです。

現実の世界ではマネージャースクリプトはもっと複雑で処理時間を要するものになるでしょう。しかし、今回のケースでは私がこのプロジェクトの開発者で、私のコードが何をするべきなのかよく知っており、どういう振る舞いが可能で、どういうことがゲーム上では起きないかということも知った上でマネージャークラスを設計することができます。Unityは残念ながらそうした知識を持っていません。

結局、どうしたらいいの?

もちろん、すべてはあなたのプロジェクト次第です。しかし現実に目を向けると非常にたくさんのGameObjectをシーン上に配置してすべてのオブジェクトで毎フレーム何らかのロジックを実行しているようなゲームを見ることは稀ではないのです。大体はそれらは大した影響はなにもないようなものですが、数が非常に大きくなってくると、数千のオブジェクトに対してUpdateメソッドを呼ぶコストはだんだん大きくなり、目に見えるようになってきます。この状態ではもしかしたらすでにゲームの設計をリファクタリングしてマネージャーパターンを導入するには遅すぎるということもあるかもしれません。

データ的にはこの記事の通りですので、次のプロジェクトの開始時にはどうするべきか考えてみてはいかがでしょうか。

76 コメント

コメントの配信登録

コメント受付を終了しました。

  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. Valentin Simonov

      2月 11, 2016 3:14 pm

      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. FrkTheMantis

      1月 7, 2016 8:38 pm

      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. Adam Tuliper

          1月 15, 2016 11:30 pm

          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. Mahan Moghaddam

    1月 2, 2016 7:10 pm

    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. Valentin Simonov

      12月 30, 2015 12:53 pm

      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. Richard Fine

      12月 25, 2015 3:08 am

      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. Valentin Simonov

      12月 25, 2015 7:04 am

      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. Valentin Simonov

        12月 24, 2015 12:55 pm

        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. Teal Rogers

          1月 3, 2016 9:24 pm

          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. Valentin Simonov

      12月 25, 2015 7:20 am

      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. Kailas Dierk

      12月 24, 2015 4:36 am

      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. Richard Fine

      12月 24, 2015 3:50 am

      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. Valentin Simonov

          12月 24, 2015 9:03 am

          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. Valentin Simonov

      12月 25, 2015 7:13 am

      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. Valentin Simonov

      12月 25, 2015 7:11 am

      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. Cameron Bonde

        1月 7, 2016 4:05 am

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

      3. anonymized to prevent reprisals

        1月 19, 2016 11:24 am

        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. Valentin Simonov

      12月 24, 2015 9:54 am

      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. Arthur Brussee

    12月 23, 2015 6:45 pm

    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. Valentin Simonov

      12月 24, 2015 9:19 am

      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. Arthur Brussee

      12月 23, 2015 6:56 pm

      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. Valentin Simonov

          12月 24, 2015 9:33 am

          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. Valentin Simonov

      12月 24, 2015 9:57 am

      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. Chris Sinclair

    12月 23, 2015 5:29 pm

    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. Chris Sinclair

      12月 23, 2015 5:31 pm

      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. Valentin Simonov

      12月 23, 2015 5:47 pm

      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. Robert Cummings

        12月 23, 2015 6:53 pm

        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. Morten Skaaning

          12月 24, 2015 12:51 pm

          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. Valentin Simonov

          12月 24, 2015 9:37 am

          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.