UNITY PERFORMANCE TESTS

Today, I searched online for some answers on Unity performance. Namely, I was a little curious about GetComponent.

Do I REALLY need to cache all my components? It’s tedious.

Chaos Cult Games did some tests, says that it’s really not that slow. Whew, thanks! But then a commenter says, “Behind the scenes, Unity is still indexing and working with everything by String rather than by type.”

Wait. What?! Well that’s interesting. Does that mean GetComponent(string) is FASTER? Time to do some of my own performance testing.

GetComponent() Performance

I start by firing up a new, empty project, deleting the default Camera and Directional Light, and then adding an Empty GameObject and two new C# scripts I attach onto it.

I first make a DummyComponent script. This thing does nothing.

Screen Shot 2015-06-01 at 1.28.13 PM

Then I make the actual tester. This thing does all the things.

Screen Shot 2015-06-01 at 1.34.49 PM

My GetComponentTester will do, 10,000 times, a GetComponent call. I will comment all of them out by one, and test each individually.

And the results! Lower is better.

  • GetComponent() : 10ms
  • GetComponent(Type) : 10ms
  • GetComponent(string) : 150ms

I thought you said the generic methods were slower! They’re blazing fast here. Perhaps Unity has changed since that post, which was March 2014, or perhaps they optimized this call somehow. It’s over a year later, Unity 5 is out. I can take solace in the fact that the generic versions are the way to go.

Then, on a whim I removed DummyComponent from my testing GameObject.

This happened. See that huge block on the right?

Screen Shot 2015-06-01 at 1.41.13 PM copy

Whoa whoa whoa what. The fact that GetComponent<T>() is returning null makes it much much slower. All those cycles are being sucked up by GC.Collect()! This does mean that it’s not actually GetComponent<T>() that’s taking up all that time, but the fact that it’s allocating something for the GC. The dark yellow means it’s garbage—56MB of it per frame. Granted, this is an extreme test of 100,000 runs per frame, but that’s still huge! Next I tested that versus GetComponent(string).

Screen Shot 2015-06-01 at 1.46.51 PM copy

Lower is better.

  • GetComponent<T>() : 190ms : 80ms GC
  • GetComponent(string) : 150ms : 0 GC

Interesting! Both are working on a GameObject without DummyComponent attached. The string version is 20ms faster, which is imperceptibly slight. But the big point here is that the string version is not allocating anything! There is zero garbage, while the generic version is allocating a total of over 50MB!

I went back and re-tested with DummyComponent attached, and I get no allocation for all three. That’s good.

GetComponent() Performance Results

Ordered by fastest to slowest.

  • 10ms : GetComponent()  != null
  • 10ms : GetComponent(Type) != null
  • 150ms : GetComponent(string) != null
  • 150ms : GetComponent(string) == null
  • 190ms (GC) : GetComponent() == null
  • 190ms (GC) : GetComponent(Type) == null

We see here that GetComponent() is much faster than the string versions.

MonoBehaviour Fields and Tags

In the article I linked above, they also talk about fields being slow. I was already in a testing mindset, so I decided to do my own.

I love the super convenient fields part of every MonoBehaviour. We can just do MyComponent.gameObject to get the attached GameObject. Or MyComponent.transform and etc etc. They removed MonoBehaviour.rigidbody and MonoBehaviour.renderer for Unity 5 unfortunately, but now that I see, it was probably a good thing.

Here’s my test script.

Screen Shot 2015-06-01 at 2.45.07 PM

A few things here.

I’m testing two things, with four cases. First, I’m testing the caching of a MonoBehaviour Field vs not caching it. The field I’m testing is MonoBehaviour.gameObject, which is a pretty popular field I’m guessing. I’m also testing tag comparisons. I noticed that there was a gameObject.CompareTag(string) method! Was there any advantage to this?

Fields & Tags Performance Results

Smaller bars are faster.tagcomparison

I’m proud of my little graphic here 🙂

If you squint, you’ll be able to see the differences between the graphs. First of all, the cached GameObject reference (g.tag and g.CompareTag()) are slightly faster. Again, they’re ever ever so slightly faster, and in practice they probably don’t matter at all. You can see however that tag checking is more expensive than GetComponent()! 30fps is about 33ms, and these are all hitting around that mark. They’re pretty much all the same speed. Caching .gameObject makes no difference.

What is notable as well is that the tag comparison using == has heap allocation, while CompareTag(string) does not!

This is quite weird to me.

I tested layer comparisons next—a simple

if (gameObject.layer == 0){}

Screen Shot 2015-06-01 at 2.58.46 PM

Whoa! Okay. Note that the yellow part is not allocation, it’s WaitForTargetFPS(). That means it’s going faster than 120FPS in my case. Layer comparison takes a huge fraction of the time that Tag comparison takes. Where the tag test takes about 33ms, the layer test takes 6ms. That means a layer comparison is more than five times faster than tag comparison.

So… what does this mean?

How do you apply this?

Well, first of all these tests will not accurately show real life performance, but are merely benchmarks. This applies to almost every test you can find online. With that said, we can still try to use this information.

Keep in mind these are ONLY VALID within these tests. If you find any one of these to be false in another case, let me know!

  • You should just use GetComponent<T>().
  • GetComponent<T>() is much slower if it finds nothing.
  • GetComponent(string) has a consistent speed. 
  • GetComponent(string) doesn’t generate garbage.
  • Advantage of caching fields like gameObject is ridiculously small.
  • If you are comparing tags use GameObject.CompareTag(string).
  • Layers are much much faster than tags.

In Zarvot, I have destructible objects tagged as “Destructible”. This tag tells me whether or not a Destructible component exists on that object. I did this in an effort to save on GetComponent costs. Now when I think about it, it feels like insurance against garbage generation that I’m paying with cycles.

But really, none of this matters.

In the grand scheme of things however, these are micro optimizations—before even considering doing any of these minuscule things, keep in mind that you probably won’t get any perceivable performance difference in real life applications. I did all this to amuse myself, to be honest.

Advertisements

6 thoughts on “UNITY PERFORMANCE TESTS

  1. But then he says in the comments, “Behind the scenes, Unity is still indexing and working with everything by String rather than by type.”

    That was some OTHER guy in the comments, not me (-8
    And I don’t belive it’s true (which your test confirms). Awesome idea to test for non-exsitent component by the way, I was very surprised to see your results!

    Like

  2. I gave up on GetComponent() a long time ago. While it may be fairly optimized and efficient and yadayada, doing the same work unnecessarily is wasteful, so… nope.

    My solution was to just wrap up component caching so it could be done with a one-liner, and to add some editor-configurability. I’ll write up my own blog post about all of it _someday_, but the gist of it is a MonoBehaviour extension that provides a PopulateComponentField() field you call in Awake() for each field you want to populate. Optionally, a SearchExtent-enum-typed field lets you configure in the editor how far you wish to search for the Component.

    A Gist with all the necessary pieces: https://gist.github.com/capnslipp/ac26e38fce770b5c4594

    Like

  3. The reason GetComponent() appears very slow when returning null is because it throws an exception when it cannot find anything. This only happens in the editor though. If you do the save test on mobile devices GetComponent() == null should be just as fast as its != null version.

    Like

  4. Great information! thanks!
    Just note that gameObject is probably a field of MonoBehaviour so it’s irrelevant to cache it, but suppose you cache on Start() a component on that object (via GetComponent) it should display a difference I suppose.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s