Cache transform: really needed?

Hi there,

I usually do the cache components that I need to use on my scripts by doing GetComponent on Awake() or Start(). But recently, readin the book C# Game Programming Cookbook for Unity 3D (2014), the author do the cahe even to transform.

Question: is it really necessary even in Unity 5, since the ‘transform’ is accessible directly on MonoBehavior.? We don’t have this kind of optimization encapsulated yet? And there is someway to measure the results of this optimization?

Thanks!

3 Likes

No - transform get was optimized in 5 so you no longer need to cache it yourself.

2 Likes

I’ve found myself using Transform where I used to use GameObject in a lot of cases now. Maybe he is caching it just for shorthand convenience.

Thanks @KelsoMRK . Do you know where we can find this information? Is an Unity 5 release note?

Release Notes for 5 state that GetComponent was improved across the board. I found it specifically somewhere but not sure where at this point.

http://blogs.unity3d.com/2014/06/23/unity5-api-changes-automatic-script-updating/

Last line in the article.

3 Likes

Caching components yourself is still faster than GetComponent. (This includes Transform.) In most cases it won’t really make any difference and isn’t worth bothering with, but nevertheless it’s something to keep in mind. The line in the blog about Transform being cached is not true. You have to do it yourself if you want the speed benefits.

–Eric

5 Likes

Are you sure? Our understanding was this was the exception, that it was a direct reference, since all gameObjects have transforms. Is it noted somewhere that this isn’t the case?

2 Likes

Well, this is wrong? Why don’t fix the post then?

2 Likes

I’m quite sure. It’s pretty trivial to run benchmarks or use the profiler to confirm this.

–Eric

I posted in another thread a speed test of transform, but ill put it here as well.

Click for transform speed test

using UnityEngine;
using System.Diagnostics;

public class TestTransformSpeed : MonoBehaviour
{
    Transform myTransform;
    Transform myTransformGetter {get {return myTransform;}}
    int iterations = 1000000;

    void Start()
    {
        myTransform = GetComponent<Transform>();

        TestTransform(); //result = 00:00:00.0923923 (~92 Milliseconds)
        TestMyTransform(); //result = 00:00:00.0694768 (~69 Milliseconds)
        TestMyTransformGetter(); //result = 00:00:00.0739161 (~74 Milliseconds)
    }

    void TestTransform()
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        for(int i = 0; i < iterations; i++)
            transform.position = Vector3.zero; //transform uses a Getter to retrieve the transform. Not sure whats in the getter.

        stopWatch.Stop();
        UnityEngine.Debug.Log(stopWatch.Elapsed);
    }

    void TestMyTransform()
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        for(int i = 0; i < iterations; i++)
            myTransform.position = Vector3.zero;

        stopWatch.Stop();
        UnityEngine.Debug.Log(stopWatch.Elapsed);
    }

    void TestMyTransformGetter()
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        for(int i = 0; i < iterations; i++)
            myTransformGetter.position = Vector3.zero;

        stopWatch.Stop();
        UnityEngine.Debug.Log(stopWatch.Elapsed);
    }
}

This was all done in Unity 5.0.2f1

The speed difference is small, so I wouldnt worry about it.

1 Like

I just gave it a try, and you are partially correct. Or rather, you are completely correct that it is faster to locally cache, but .transform is much faster than GetComponent (for transform). So presumably as the article says, caching is happening, there just is a small amount of overhead. Good to know. Thanks!

            GetComponent<Transform>()
            ----------------------------------
            00:00:00.1267660
            00:00:00.1392190
            00:00:00.1192030
            00:00:00.1212520
            00:00:00.1171210
            00:00:00.1203210
            00:00:00.1212310
            00:00:00.1162110
            00:00:00.1318870
            00:00:00.1318870

            .transform
            ----------------------------------
            00:00:00.0746700
            00:00:00.0889320
            00:00:00.0816240
            00:00:00.0850060
            00:00:00.0813800
            00:00:00.0728400
            00:00:00.0861790
            00:00:00.0867240
            00:00:00.0838280
            00:00:00.0851020

            myTransform (cached)
            ----------------------------------
            00:00:00.0731950
            00:00:00.0631190
            00:00:00.0667700
            00:00:00.0701300
            00:00:00.0640030
            00:00:00.0652790
            00:00:00.0628240
            00:00:00.0586720
            00:00:00.0650240
            00:00:00.0634400
12 Likes

Nice test. But you only set the position to 0,0,0. What about some randoms operations in Scale and Rotation too?

I dont see what that would prove. The test is only to see which call to transform is faster. What you do afterwards is irrelavent as long as its the same for each test.

6 Likes

For the purposes of the test these would simply cloud the issue with more overhead. Setting the position to 0,0,0 is probably the most consistent operation that incurs the smallest penalty.

That’s true. I thought the compiler could be doing some pre-optimization because it can ‘see’ the fixed operation there, and just replicate a previous result to the next one on the loop, generating a small variation between them.

I wish the compiler was that smart.

2 Likes

Heh, just did a quick couple of tests using Random.rotation for kicks and most of the results were so high that the variance between the runs was larger than the difference in between the different versions of how the transform was being reference was being called.

Got an ANOVA for us? :slight_smile:

1 Like

Just ran some tests on this in Unity 2017.1, with the new 4.6 .NET compiler, if anyone is interested.

Doing 100,000 calls to GetComponent and GetComponent and GetComponent:
Transform = 4ms (.00004ms / call)
Rigidbody = 4ms
Foo = 14ms (.00014ms / call)

So it appears as those native components are cached somehow, or at least about 3x faster to lookup than custom scripts, and while the custom GetComponent is still fairly fast, I think the performance hit still justifies caching, especially in your update loop.

Using a design pattern like this, you can get your custom scripts down to the same performance as native, without much extra code:

public T GetCachedComponent<T>(ref T c) where T : Component {
    if (!c) { c = GetComponent<T>(); }
    return c;
}

Foo foo;
public Foo Foo { get { return GetCachedComponent(ref foo); } }

Bar bar;
public Bar Bar { get { return GetCachedComponent(ref bar); } }

//etc

It has one big flaw though, if your component does not exist, performance spikes by 10x.

Doing 100,000 calls to GetComponent(), takes 450+ms(!), so you still need to be very careful you’re not calling this each frame to check if the component exists, it could burn you.

Overall, due to the very high costs of checking for a missing component, and the extra cost for custom components, I think best practice remains to manually inject your component dependancies in the editor, or wire them up once in your Start() method.

3 Likes