C# - The performance of Properties vs Fields

created: 04.08.2020

When you want to expose something publicly in C#, you should generally use properties instead of fields, for reasons like:

But I was always a bit hesitant about putting properties everywhere, because I was afraid the performance could be worse. There are some people who measured this, for example Jackson Dunstan, so I’m just going to steal his code. I want to measure more variations: value types and reference types, public and private, read and write, and I want to know this for newer versions of dotnet core. I am going to use BenchmarkDotNet for benchmarks, and Jackson’s benchmarks translated to BenchmarkDotNet look like this:

using BenchmarkDotNet.Attributes;
public class ClassInternalBenchmark
{
	private const long NumValues = 1000000000;
	public long SumAuto { get; private set; }
	private long sum;
 
	[Benchmark]
	public void SumProperty()
	{
		for (long i = 0; i < NumValues; ++i)
		{
			SumAuto += i;
		}
	}
	[Benchmark]
	public void SumField()
	{
		for (long i = 0; i < NumValues; ++i)
		{
			sum += i;
		}
	}
	 
	[Benchmark]
	public void SumLocal()
	{
		long localSum = 0;
		for (long i = 0; i < NumValues; ++i)
		{
			localSum += i;
		}
		sum = localSum;
	}
}

Here I’m comparing how long it takes to write to a property/field within the class in which it’s defined and the speed of just using a local variable.

Here are the results:

Method Mean Error StdDev
SumProperty 1,888.6 ms 9.14 ms 8.10 ms
SumField 1,929.7 ms 0.25 ms 0.22 ms
SumLocal 538.5 ms 0.09 ms 0.07 ms

Note: All benchmarks are run on linux using dotnet core 3.1 and an AMD Phenom II X6 1100T processor. If you want to run the benchmarks on a different platform, the source is here.


Using a local variable is unsurprisingly the fastest, fields and properties are very close to each other performance wise. But wait, how are properties actually a little bit faster than fields in this benchmark? If you check the generated assembly from the jit for these functions, you will notice that the generated code is almost the same for both functions, they just read and write to different memory adresses. In the later benchmarks I used separate classes instead of having a single class with properties and fields. I also tried to make some more complete benchmarks, measuring read and write speed independently.

To summarize the results from all benchmarks: Most of the time, the difference between fields and properties is small or completely optimized away, but there is a case where it matters, virtual properties.

Virtual Properties

Normal properties can often be as fast as fields because they are inlined if they aren’t doing anything. Now virtual properties can’t be inlined and have to go through vtable lookup, so they are significantly slower:

Method Mean Error StdDev
ReadFields 4.645 ns 0.0021 ns 0.0016 ns
ReadProperties 4.890 ns 0.0314 ns 0.0262 ns
ReadVirtualProperties 16.222 ns 0.0019 ns 0.0016 ns
ReadUnoptimizedProperties 4.822 ns 0.0011 ns 0.0010 ns
ReadNoInlineProperties 14.050 ns 0.0023 ns 0.0021 ns
WriteFields 5.806 ns 0.0013 ns 0.0011 ns
WriteProperties 5.789 ns 0.0008 ns 0.0007 ns
WriteVirtualProperties 15.213 ns 0.0040 ns 0.0036 ns
WriteUnoptimizedProperties 5.788 ns 0.0014 ns 0.0012 ns
WriteNoInlineProperties 14.250 ns 0.0034 ns 0.0030 ns

As you can see, the difference between fields and properties is negligible, but virtual properties take significantly more time. To test my hypothesis that this is because of inlining, I created two more test cases: one where the property was marked to not be inlined, and one where other optimizations were disabled using the MethodImplOptions. From the table above you can see that the property that wasn’t inlined was almost as slow as the virtual property, while the other optimizations had no effect.


Warning: The inlining is not done by the C# -> CIL compiler, but by the JIT. So runtime options can have an influence on the performance.


Conclusion:

If you have problems with the performance of properties:

But most of the time, properties will be the better choice. Before you decide to use a public field make sure you really, really need to. Always profile your code first. Maybe it’s only slower in debug? Even the standard library does it sometimes, but most of the time it’s not a good idea.