Search code examples
c#optimizationclrcil

How does CLR optimize property references?


I've been a programmer for a long time and have recently gotten a job writing C#. I was curious to see if Visual Studio optimized property calls to simple memory moves instead of performing a function call and return. So I wrote a program that had two versions of a 3D point class with a method to compute the magnitude: one version accesses the fields directly and one uses properties. I ran both with 100,000,000 points and they took the same amount of time. But when I use ildasm to look at the generated code, the version using properties seems to use a function call to access the property value. (This is a Release build, so code optimization is turned on.)

My questions:

  1. Is the function call to get_X being optimized to a memory move at runtime? (It would seem so since it takes the same amount of time to execute as direct field references.)

  2. Is there a way, using ildasm or another tool, to see which optimizations occur at runtime?

I have tried running the process without the debugger then attaching to the process but VS2017 says "No disassembly available".

The version that calls private fields directly:

.method public hidebysig instance float64
          Abs() cil managed
{
    // Code size       47 (0x2f)
    .maxstack  8
    //000052:             return Math.Sqrt(_x * _x + _y * _y + _z * _z);
    IL_0000:  ldarg.0
    IL_0001:  ldfld      float64 CPUTests.Point3d::_x
    IL_0006:  ldarg.0
    IL_0007:  ldfld      float64 CPUTests.Point3d::_x
    IL_000c:  mul
    IL_000d:  ldarg.0
    IL_000e:  ldfld      float64 CPUTests.Point3d::_y
    IL_0013:  ldarg.0
    IL_0014:  ldfld      float64 CPUTests.Point3d::_y
    IL_0019:  mul
    IL_001a:  add
    IL_001b:  ldarg.0
    IL_001c:  ldfld      float64 CPUTests.Point3d::_z
    IL_0021:  ldarg.0
    IL_0022:  ldfld      float64 CPUTests.Point3d::_z
    IL_0027:  mul
    IL_0028:  add
    IL_0029:  call       float64 [mscorlib]System.Math::Sqrt(float64)
    IL_002e:  ret
} // end of method Point3d::Abs

The version that calls properties, followed by the get_X method:

.method public hidebysig instance float64
          Abs() cil managed
{
    // Code size       47 (0x2f)
    .maxstack  8
    //000052:             return Math.Sqrt(X * X + Y * Y + Z * Z);
    IL_0000:  ldarg.0
    IL_0001:  call       instance float64 CPUTests.Point3dProperties::get_X()
    IL_0006:  ldarg.0
    IL_0007:  call       instance float64 CPUTests.Point3dProperties::get_X()
    IL_000c:  mul
    IL_000d:  ldarg.0
    IL_000e:  call       instance float64 CPUTests.Point3dProperties::get_Y()
    IL_0013:  ldarg.0
    IL_0014:  call       instance float64 CPUTests.Point3dProperties::get_Y()
    IL_0019:  mul
    IL_001a:  add
    IL_001b:  ldarg.0
    IL_001c:  call       instance float64 CPUTests.Point3dProperties::get_Z()
    IL_0021:  ldarg.0
    IL_0022:  call       instance float64 CPUTests.Point3dProperties::get_Z()
    IL_0027:  mul
    IL_0028:  add
    IL_0029:  call       float64 [mscorlib]System.Math::Sqrt(float64)
    IL_002e:  ret
} // end of method Point3dProperties::Abs

.method public hidebysig specialname instance float64
          get_X() cil managed
{
    // Code size       7 (0x7)
    .maxstack  8
    //000016:             get { return _x; }
    IL_0000:  ldarg.0
    IL_0001:  ldfld      float64 CPUTests.Point3dProperties::_x
    IL_0006:  ret
} // end of method Point3dProperties::get_X

Solution

  • In his book, CLR via C#, Jeffrey Richter writes:

    For simple get and set accessor methods, the just-in-time (JIT) compiler inlines the code so that there’s no run-time performance hit as a result of using properties rather than fields.

    CLR via C# is kind of my bible, so for me, this is more than enough proof that they are inlined.

    ildasm can only show you the end result of the optimizations that happen at compile time. If you want to check the runtime optimizations, you have to actually look at the runtime code, that is, the code that is generated from the assembly and is being run. S.O.S extension for WinDbg might be a tool that can help you.