CLR 如何优化属性引用?

SSt*_*eve 2 c# clr optimization cil

我做了很长时间的程序员,最近找到了一份编写 C# 的工作。我很想知道 Visual Studio 是否优化了对简单内存的属性调用,而不是执行函数调用和返回。所以我写了一个程序,它有两个版本的 3D 点类和一个计算量级的方法:一个版本直接访问字段,一个版本使用属性。我用 100,000,000 点跑了两个,他们花了相同的时间。但是当我使用ildasm查看生成的代码时,使用属性的版本似乎是使用函数调用来访问属性值的。(这是一个发布版本,所以代码优化是打开的。)

我的问题:

  1. 对 get_X 的函数调用是否在运行时针对内存移动进行了优化?(看起来是这样,因为执行直接字段引用所需的时间相同。)

  2. 有没有办法使用ildasm或其他工具来查看运行时发生了哪些优化?

我尝试在没有调试器的情况下运行该进程,然后附加到该进程,但 VS2017 显示“没有可用的反汇编”。

直接调用私有字段的版本:

.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
Run Code Online (Sandbox Code Playgroud)

调用属性的版本,后跟get_X方法:

.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
Run Code Online (Sandbox Code Playgroud)

Reg*_*lez 5

在调试时使用反汇编视图可能会显示属性 getter 和 setter 确实是内联的。您可以在 Visual Studio 中使用 CTRL-ALT-D 访问它。

如果您想发现抖动优化,可以使用MethodImplOptions禁用它们。

public struct foo
    {
        private int _bar;
        public int bar
        {
            [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
            get { return _bar; }
            [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
            set { _bar = value; }
        }
    }
Run Code Online (Sandbox Code Playgroud)

将在调试时向您展示:

在此处输入图片说明

而如果你不放属性,你甚至不能打断点,因为所有的getter都被内联了。