-
Notifications
You must be signed in to change notification settings - Fork 16
Performance Guide
Tip
Lists: for loop is generally faster.
Arrays: foreach loop can be as fast or sometimes faster than for.
1, Lists: In Unity, for is usually faster than foreach when iterating over List objects. This is because foreach creates an enumerator object, which can add a slight overhead, while for directly accesses the elements by index. This extra allocation in foreach can impact performance slightly, especially in scenarios where garbage collection (GC) overhead is a concern.
2, Arrays: For arrays, the foreach loop can sometimes be as fast as, or even faster than, a for loop. This is because the C# compiler optimizes foreach for arrays, making it nearly equivalent to a for loop. Since arrays are fixed-size and their elements are stored in a contiguous block of memory, the compiler can generate very efficient IL (Intermediate Language) code for foreach on arrays, often making it just as efficient as a for loop.
- When to Prefer One Over the Other
- Use for with Lists: For cases where you’re iterating over large lists or have performance-sensitive code, for is generally preferred for List. This can avoid the extra enumerator allocation that comes with foreach.
- Use foreach with Arrays: If you are iterating over arrays, foreach is often just as performant as for, and it can make your code more readable.
Be careful with classes or methods that take IEnumerable as an argument (like System.Linq
, or List's AddRange
...) GC.Alloc is allocated here.
You can create extension methods that accept input arguments of specific data types instead of IEnumerable<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> Adds<T>(this List<T> source, List<T> entries)
{
for (int i = 0; i < entries.Count; i++)
{
source.Add(entries[i]);
}
return source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> Adds<T>(this List<T> source, T[] entries)
{
foreach (var e in entries)
{
source.Add(e);
}
return source;
}
Note
Unity has two memory spaces: native memory and managed memory. Resources loaded by the Unity engine are placed in native memory, and strings and binary data loaded on the C# side are placed in managed memory.
When you call GetComponent
of a GameObject
it will query the data in native memory
and return the address of that component (this address is stored in managed memory
). When this GameObject
is destroyed the component's data in native memory will be deleted but the information about its address in the previous managed memory
will not.
So you should assign null to unity object variables after using them.
var player = GetComponent<Player>();
// TO_DO
player = null;
That's why you should be careful when comparing Unity.Object
to null
read more infomation here or here
Tip
In short we will try to use the null conditional operator even with objects that have been destroyed.
/// <summary>
/// m_MyGameObject is not truly null, but rather a sort of “null simulator” <br/>
/// Then you can call: <br/>gameObject.OrNull()?.DoSomething();
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T OrNull<T>(this T source) where T : Object { return source == null ? null : source; }
now you can use
transform.OrNull()?.Translate(new Vector3(1, 0, 1));