I have an immutable struct I'm using for a 3D vector. I am aware that basic getter properties are (supposed to be) inlined and therefore should perform identical to fields assuming you are in release configuration and running outside of VS without any influence from debugging or JIT suppression. However, that's not the behavior I'm seeing and I'm trying to figure out why. (By the way, I've read every other post about this on SO that I can find to no avail).
Setup: VS2019 v16.8.4, using .NET 5.0 & C#9. Release configuration, with Optimization enabled in all projects.
First, the relevant piece of my vector class using public fields (for comparison):
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct Vector3
{
public readonly double x;
public readonly double y;
public readonly double z;
...
}
and the Console app code to test the time to access a field from 1 million vectors. (I know I can do things to make the benchmark code better, such as increasing thread priority or warming up the function, but the key here is the difference between the performance I see when using fields vs. properties--which is consistent):
static void Main(string[] args)
{
const int sampleSize = 1000000;
var vectors = new Vector3[sampleSize];
// Fill with random values.
var rand = new Random();
for (var i = 0; i < sampleSize; i++)
{
vectors[i] = new Vector3(rand.NextDouble(),
rand.NextDouble(),
rand.NextDouble());
}
double val;
var sw = Stopwatch.StartNew();
for (var i = 0; i < sampleSize; i++)
val = vectors[i].x; //Access the field as a test.
sw.Stop();
Console.WriteLine($"Accessing the fields 1M times took {sw.ElapsedTicks} ticks.");
When I build this code in release configuration, start the command prompt, and run the executable, it takes around 3100 to 3400 ticks to access the x field a million times.
If I change this code to use a simple getter instead:
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct Vector3
{
public double X { get; }
public double Y { get; }
public double Z { get; }
...
}
and the only change to Main()
is this:
val = vectors[i].X; //Now access via a property instead of the field.
(that is, accessing via the auto-prop instead of the field) then when I build this in release config and run the executable several times in the command prompt, I see times in the 15,000 tick range, or about 5 times slower!
Again, I have confirmed for both tests that I am compiling in release configuration, with optimization checked in all of my projects, and I am running the executable in the command prompt outside of Studio (so the debugger can't possibly be attached).
What am I missing? Why is the property access taking 5 times longer?