Unity and Reflection – Optimising Memory using Caching on iOS

Summary

Reflection

I really love reflection. Reflection is a technique used for obtaining type information at run-time. It’s not only that, with reflection is possible to examine and change information of objects, to generate (technically to emit IL) new classes, methods and so on still at runtime. It’s a powerful technique but it is known, under certain circumstances, for being slow. If you are a game developer and you are targeting mobile devices (iOS or Android for instance) using Unity, you definitely want to preserve your memory and save precious clock cycles. Moreover, with AOT (Ahead of Time compilation)  IL cannot be emitted at run-time as it is pre-generated at compile time. Therefore a large part of reflection, e.g. expression trees, anonymous types etc., is just not available.

The Problem

Recently I was working on a dynamic prefab serializer and I needed to use reflection to retrieve types from their string representations. In general to retrieve a type in C# you have three options:

  • typeof(MyClass), which is an operator to obtain a type known at compile-time.
  • GetType() is a method you call on individual objects, to get the execution-time type of the object.
  • Type.GetType(“Namespace.MyClass, MyAssembly”) gives you a type from its string representation at runtime.

typeof is converted into a constant at compile time and GetType  returns a reference to the run-time type of your object. But what about Type.GetType(string)???

A colleague of mine suggested to check that Type.GetType(string), formerly the method I needed to use, is not too slow and that it doesn’t generate garbage either. Mobile game developers must be very careful about this aspect. If the garbage collector kicks in to release unused/unreferenced  objects within a game loop, where each frame must be no longer than 16 ms, it likely will  create stutters and it will bring down the frame rate resulting in an unpleasant experience. While this aspect might not be a problem on PCs, it is instead a big deal on mobile devices.

I created a Unity project with a single game object, the main camera, and I attached a simple MonoBehaviour script. I put in the Update function a call to Type.GetType(“DummyClass”) and I observed the memory trend in the Unity profiler. This is the MonoBehaviour script:

public class GetTypeTest : MonoBehaviour {
   void Update () { 
      var type = Type.GetType ("DummyClass");
   }
}
public class DummyClass{ 
}

DummyClass is an empty class that I used just for testing. Running the project in the Unity Player and looking at the profiler I noticed that no garbage was generated (look at Figure 1) and the time taken was very good too (~0.05ms).

ProfilerUnityMac

Figure 1 – Profiler on Yosemite – Mac

At that stage I was very confident to adopt the same approach on mobile too. However, I still wanted to try it on iOS and see if the outcome would be the same. See for yourself in Figure 2 🙂

ProfilerUnityIOS

Figure 2 – Profiler on iOS – iPad 4

Incredibly, the same exact call to Type.GetType(string) generates 178 bytes of garbage each iteration, forcing the GC to collect memory at some stage. Time taken to execute it is approximately 0.5 ms. Retrieving a type from its string representation generates indeed garbage on iOS!!!

The Solution

However, I still wanted and needed to use Type.GetType(string) to avoid a lot of code duplication. The only workaround that I thought of (and I like to see in comments your solutions or ideas too 🙂 ) was to use a very simple cache mechanism.

Here is the code:

 public static class TypeCache {
     private static readonly Dictionary<string, Type> _typeCache 
         = new Dictionary<string, Type>();

     public static Type GetType(string typeName) {
         Type type = null;
         if(_typeCache.TryGetValue(typeName, out type)) {
            return type;
          }

         type = Type.GetType(typeName);
         if(type != null) {
            _typeCache.Add(typeName, type);
         }
         return type;
     }
}

What this code does is very simple. If the requested type is not in cache, defined as a readonly static dictionary, then the type is retrieved and after stored into the cache. If the requested type is instead already in cache then…well it’s just simply returned.

This mechanism generates garbage too, of course, but with an improvement. 178 bytes (or more depending on the type) are generated only the first time you request a type that has never been requested before.

Results

I tested this solution on a iPad 4. The solution was compiled using Unity 5.2.1f1 and the iOS app generated using IL2CPP. My goal was to determine if that worked well, if it wasn’t too slow and most importantly if there weren’t memory leaks of any kind. As you can see in Figure 3, on the first access, 2.7KB of memory were allocated for initialising the cache object, but on successive accesses no garbage was generated anymore (see Figure 4)! Moreover the time to retrieve a type went from 0.5ms to 0.03ms, almost the same time it took on a Mac!!!

ProfilerFirstFrame

Figure 3 – First frame

ProfilerSecondAccess

Figure 4 – Later access

Conclusions

As you see, using reflection on iOS might be dangerous. You should always check that no hidden garbage is generated outside of your control. Sometimes, a very simple cache mechanism, like the one I implemented, can literally save your day. I hope and looking forward to seeing your thoughts about this topic.

See you on the next coding adventure!

2 thoughts on “Unity and Reflection – Optimising Memory using Caching on iOS

  1. I believe this code is doing the key-lookup twice:
    if(_typeCache.ContainsKey(typeName))
    return _typeCache [typeName];

    ‘ContainsKey’ is doing a ‘FindEntry’ internally and the indexer is doing the ‘FindEntry’ as well. You can avoid looking up that entry twice by using TryGetValue instead:

    Type type;
    if (_typeCache.TryGetValue(typeName, out type))
    return type;

    You can take a look at the ContainsKey, indexer and TryGetValue implemention of the .NET Framework here:
    http://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,bcd13bb775d408f1

    Liked by 1 person

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