我很长时间以来一直是程序员,并且最近找到了编写C#的工作。我很想知道Visual Studio是否优化了对简单内存移动的属性调用,而不是执行函数调用和返回。所以我写了一个程序,它有两个版本的3D点类,有一个计算幅度的方法:一个版本直接访问字段,一个使用属性。我跑了100,000,000分,他们花了相同的时间。但是当我使用 ildasm 来查看生成的代码时,使用属性的版本似乎使用函数调用来访问属性值。 (这是发布版本,因此启用了代码优化。)
我的问题:
是否将get_X的函数调用优化为运行时的内存移动? (它似乎是这样,因为它需要与直接字段引用相同的执行时间。)
有没有办法,使用 ildasm 或其他工具,看看哪些优化在运行时发生?
我已尝试在没有调试器的情况下运行该过程,然后附加到该过程,但VS2017说“没有可用的反汇编”。
直接调用私有字段的版本:
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_XML);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
System.out.println(responseEntity);
调用属性的版本,后跟.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
方法:
get_X
答案 0 :(得分:4)
在调试时使用反汇编视图可能会显示属性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; }
}
}
会在调试时显示:
如果你没有放置属性,你甚至无法点击断点,因为所有的getter都已内联。
答案 1 :(得分:2)
杰弗里里希特在他的书“CLR通过C#”中写道:
对于简单的get和set访问器方法,即时(JIT) 编译器内联代码,以便没有运行时性能 由于使用属性而不是字段。
CLR通过C#是我的圣经,所以对我而言,这足以证明它们是内联的。
ildasm只能显示编译时发生的优化的最终结果。如果要检查运行时优化,则必须实际查看运行时代码,即从程序集生成并正在运行的代码。 S.O.S对WinDbg的扩展可能是一个可以帮助你的工具。