Hello all,
I was doing some simple performance test to determine just how much slower properties are than fields in Unity, and I came across something interesting. In my test, using properties was faster than using fields! As you can see in the script posted below, I created a simple class with an integer array field (called a), and a property for that field (called A). I then tested two methods: The first was to assign the array value to another variable (called t) directly through the public field a, while the second made the assignment via the property. These test were performed in a standalone build and I got the results from the output log after playing the game. In the editor, the property access method is always about twice as slow as the public field access method.
I actually tested some other things as well, such as auto-properties, assigning the public field value to a temporary variable and then assigning that temporary variable to t, and finally using an integer rather than an integer array. All outcomes were virtually the same as the first two methods I described. That is, in repeated test of 10000 assignments/frame, the public field access method came in at about 205 ms and the property access method came in at about 175 ms.
I was under the impression that properties should be slower than fields, so why is that not the case here? Has anyone experienced similar results, or have a technical explanation to why this is happening? Thanks!
And here’s the code I used to test. I found this code online (and adjusted it slightly), it seems like it does a good job, but what do I know (I’m a noob at C#)?
using UnityEngine;
using System.Collections;
using System.Threading;
using System.Diagnostics;
public class BenchMarker : MonoBehaviour {
public int iterations = 0;
class Testing
{
public int[] a = new int[1];
public int[] A
{
get{return a;}
private set{a = value;}
}
public Testing(int t){A[0] = t;}
}
// Update is called once per frame
public void Update () {
Testing x = new Testing(2);
Stopwatch stopwatch = new Stopwatch();
Process.GetCurrentProcess().ProcessorAffinity = new System.IntPtr(2); // Uses the second Core or Processor for the Test
Process.GetCurrentProcess().PriorityClass =
ProcessPriorityClass.High; // Prevents "Normal" processes
// from interrupting Threads
Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest; // Prevents "Normal" Threads
// from interrupting this thread
//int a = x.A[0];
int t;
long avg = 0;
UnityEngine.Debug.Log("");
UnityEngine.Debug.Log("");
UnityEngine.Debug.Log("Option 1");
stopwatch.Reset();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < 1200) // A Warmup of 1000-1500 mS
// stabilizes the CPU cache and pipeline.
{
for(int i = 0; i < iterations; i++)
{
t = x.a[0];
}
}
stopwatch.Stop();
for (int repeat = 0; repeat < 20; ++repeat)
{
stopwatch.Reset();
stopwatch.Start();
for(int i = 0; i < iterations; i++)
{
t = x.a[0];
}
stopwatch.Stop();
UnityEngine.Debug.Log("Ticks: " + stopwatch.ElapsedTicks +
" mS: " + stopwatch.ElapsedMilliseconds);
avg += stopwatch.ElapsedTicks;
}
UnityEngine.Debug.Log("Avg = " + avg/20 + "ms");
UnityEngine.Debug.Log(x.ToString()); // prevents optimizations (current compilers are
avg = 0;
UnityEngine.Debug.Log("");
UnityEngine.Debug.Log("");
UnityEngine.Debug.Log("Option 2");
stopwatch.Reset();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < 1200) // A Warmup of 1000-1500 mS
// stabilizes the CPU cache and pipeline.
{
for(int i = 0; i < iterations; i++)
{
t = x.A[0];
}
}
stopwatch.Stop();
for (int repeat = 0; repeat < 20; ++repeat)
{
stopwatch.Reset();
stopwatch.Start();
for(int i = 0; i < iterations; i++)
{
t = x.A[0];
}
stopwatch.Stop();
UnityEngine.Debug.Log("Ticks: " + stopwatch.ElapsedTicks +
" mS: " + stopwatch.ElapsedMilliseconds);
avg += stopwatch.ElapsedTicks;
}
UnityEngine.Debug.Log("Avg = " + avg/20 + "ms");
UnityEngine.Debug.Log(x.ToString()); // prevents optimizations (current compilers are
UnityEngine.Debug.Break();
}
}