MonoBehavior calls optimization

发表于2017-08-07
评论0 3.8k浏览
 MonoBehavior callsoptimization

What if I told youthat Unity can be wasting a lot of CPU performance just by calling yourMonoBehaviour functions? It doesn’t really matter what your scripts are doing.If you have hundreds or thousands of them, you should be aware thatthere’s a new field of optimization.

Magic methods

MonoBehaviour functions calls are slow. And I’m talking about functions like Update()LateUpdate(), OnRender()etc. They are so-called magicmethods, and if you’re familiar with object-orientedprogramming languages, this concept looks like calling a method usingreflection mechanism (reflection enables method calls even if you don’tknow the interface). Reflection calls are expensive, so Unity does everythingthat is possible to cache any operations, so the set of CPU instructions neededto call a magic method each frame could be minimal. But it can still be slow,very slow…

Why is it so slow?

I’m not gonna talkabout the details (but if you really want to read about the details, lookat the end of this article for the links), so just imagine that Unity tries tobe as flexible and easy to use as possible. Making something easily costs CPUpower because the engine cannot make any assumptions about your game and itneeds to do a bunch of checks to call your magic functions on the rightobjects, in the right order, and to not crash in the meantime.

Can it become faster?

Oh this is my favoritepart. Yes! It can! How? You have to take the responsibility of calling Update()function by defining your own function and calling it from a manager. This way,you’re taking responsibility for updating your objects. How much faster it canbecome? Well, it depends on the platform. Let me use the measurements done byValentin Simonov onofficial Unity blog:

Mono with fast but noexception settings.

Here you seethat the difference can be worth the time. This is a measurementof calling Update() 10000 times.

Writing a manager

I will present a fairysimple example of a manager called BoxManager that ismanaging BoxManagedscripts. Manager has two responsibilities:

  1. Keeping the list of managed objects updated
  2. Calling update-like functions on managed objects when manager Update() is called.

The code may look likethis:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

 

public class BoxManager : MonoBehaviour

{

public static BoxManager Instance { get; private set; }

 

public List _managedBoxes = new List();

 

void Awake()

{

Instance = this;

}

 

void Update()

{

// update objects here

}

 

public void Register(BoxManaged box)

{

_managedBoxes.Add(box);

}

 

public void Unregister(BoxManaged box)

{

_managedBoxes.Remove(box);

}

}

As you can see, it’sreally simple. Before implementing Update() function let’s take a look at BoxManaged.cs.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

using UnityEngine;

 

public class BoxManaged : MonoBehaviour {

 

private Vector3 _position;

 

private Transform _transform;

 

void OnEnable()

{

BoxManager.Instance.Register(this);

}

 

void OnDisable()

{

BoxManager.Instance.Unregister(this);

}

 

public void ManagedUpdate()

{

// do what you normally do in Update here

}

}

It registers itselfwhen enabled and de-registers when disabled. Fair enough. ManagedUpdate()function is a function that will replace Update() magic function. Let’simplement BoxManager.Update(), so it will be able to call all BoxManaged.ManagedUpdate() atonce.

 

1

2

3

4

5

6

7

void Update()

{

for (int i = 0; i < _managedBoxes.Count; i)

{

_managedBoxes[i].ManagedUpdate();

}

}

And that’s it! Really!Now in ManagedUpdate() you can do everything you would normally do in theUpdate() function.

Please note that I didnot use foreach for iterations. Firstly, because it’s generatingsmall amount garbage Unity’s version of Mono. Secondly, because it simplyseems to be slower.

Should I care?

It depends onwhat kind of game are you creating and what is the target platform. Askyourself a question – do you have many MonoBehaviour objects with Update()calls? It doesn’t necessarily need to be Update(), it can be anything that itis invoked with each frame. Then, if you’re targeting mobiles, it’s definitelyworth to try! Targeting standalones? It’s still something you may consider,especially if you’re planing to have huge amount of objects.

Sometimes you may needa manager even if you’re have a relatively small amount of objects. On iOSthere was (I don’t know if it has been fixed or not) a problem with OnRender() function. Having it on 30-40 objects could decrease the game’s performancetwice! The solution? A manager like the one presented above, but instead ofcalling Update() it should be calling OnRender() code. Yes, it willwork!

Please keep in mindthat this is one of many optimization strategies that you can use. Yetthis one is quite hidden – unless you know about it, you will have a hardtime to find about this one. That’s the reason why this article hasbeen brought to life.


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: