Optimizing Script Performance
This page gives some general hints for improving script performance on iOS.
Reduce Fixed Delta Time
Use a fixed delta time value between 0.04 and 0.067 seconds (ie, 15-25 frames per second). You can change this in . This reduces the frequency with which FixedUpdate is called and how often the physics engine has to perform collision detection and rigidbody updates. If you are using a rigidbody for the main character, you can enable interpolation in the Rigidbody Component to smooth out low fixed delta time steps.
Reduce GetComponent Calls
Using GetComponent or built-in component accessors can have a noticeable overhead. You can avoid this by getting a reference to the component once and assigning it to a variable (sometimes referred to as "caching" the reference). For example, if you are using something like:-
function Update () { transform.Translate(0, 1, 0); }
...you would get better performance by changing it to:-
var myTransform : Transform; function Awake () { myTransform = transform; } function Update () { myTransform.Translate(0, 1, 0); }
Avoid Allocating Memory
You should avoid allocating new objects unless you really need to, since they increase the garbage collection overhead when they are no longer in use. You can often reuse arrays and other objects rather than allocate new ones and doing so will help to minimise garbage collection. Also, you should use structs instead of classes where possible. Struct variables are allocated from the stack like simple types rather than from the heap like object types. Since stack allocation is faster and involves no garbage collection, structs will often improve performance if they are fairly small in size. While large structs will still avoid allocation/collection overhead, they will incur a separate overhead due to "pass-by-value" copying and may actually be less efficient than the equivalent object classes.
Minimise the GUI
The GUILayout functions are very convenient for automatic spacing of GUI elements. However, this automation naturally comes with a processing overhead. You can avoid some of this overhead by handling the layout manually using the GUI functions. Additionally, you can set a script's useGUILayout variable to false in order to disable the layout phase completely:-
function Awake () { useGUILayout = false; }
Use iOS Script Call Optimization
Most of the functions in the UnityEngine namespace are implemented in C/C++. Calling a C/C++ function from a Mono script involves a performance overhead. You can use iOS Script Call optimization (menu: ) to save about 1 to 4 milliseconds per frame. The options for this setting are:-
- Slow and Safe - the default Mono internal call handling with exception support.
- Fast and Exceptions Unsupported - a faster implementation of Mono internal call handling. However, this doesn't support exceptions and so should be used with caution. An app that doesn't explicitly handle exceptions (and doesn't need to deal with them gracefully) is an ideal candidate for this option.
Optimizing Garbage Collection
As mentioned above, it is best to avoid allocations as far as possible. However, given that they can't be completely eliminated, there are two main strategies you can use to minimise their intrusion into gameplay:-
Small heap with fast and frequent garbage collection
This strategy is often best for games that have long periods of gameplay where a smooth framerate is the main concern. A game like this will typically allocate small blocks frequently but these blocks will be in use only briefly. The typical heap size when using this strategy on iOS is about 200KB and garbage collection will take about 5ms on an iPhone 3G. If the heap increases to 1MB, the collection will take about 7ms. It can therefore be advantageous sometimes to request a garbage collection at a regular frame interval. This will generally make collections happen more often than strictly necessary but they will be processed quickly and with minimal effect on gameplay:-
if (Time.frameCount % 30 == 0) { System.GC.Collect(); }
However, you should use this technique with caution and check the profiler statistics to make sure that it is really reducing collection time for your game.
Large heap with slow but infrequent garbage collection
This strategy works best for games where allocations (and therefore collections) are relatively infrequent and can be handled during pauses in gameplay. It is useful for the heap to be as large as possible without being so large as to get your app killed by the OS due to low system memory. However, the Mono runtime avoids expanding the heap automatically if at all possible. You can expand the heap manually by preallocating some placeholder space during startup (ie, you instantiate a "useless" object that is allocated purely for its effect on the memory manager):-
function Start() { var tmp = new System.Object[1024]; // make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks for (var i : int = 0; i < 1024; i++) tmp[i] = new byte[1024]; // release reference tmp = null; }
A sufficiently large heap should not get completely filled up between those pauses in gameplay that would accommodate a collection. When such a pause occurs, you can request a collection explicitly:-
System.GC.Collect();
Again, you should take care when using this strategy and pay attention to the profiler statistics rather than just assuming it is having the desired effect.
Page last updated: 2011-10-11