场与财产。 性能优化

请注意这个问题只与性能有关。 让我们跳过devise指南,哲学,兼容性,可移植性以及任何与纯粹性能无关的东西。 谢谢。

现在的问题。 我一直认为,因为C#getters / setter实际上是伪装的方法,所以阅读public字段必须比调用getter更快。

所以要确保我做了一个testing(下面的代码)。 但是, 如果您在Visual Studio中运行该testing,则此testing只会产生预期的结果(即字段比34%的获得者更快 )。

一旦你从命令行运行它显示几乎相同的时间…

唯一的解释可能是CLR做了额外的优化(纠正我,如果我在这里是错的)。

我不相信在实际的应用中,这些属性以更复杂的方式被使用,它们将以相同的方式被优化。

请帮助我certificate或反驳现实生活中属性比田野慢的观点。

问题是 – 我应该如何修改testing类来使CLR更改行为,使公共领域超越获取者。 或者告诉我,没有内部逻辑的任何属性将会像字段一样执行(至less在getter上)

编辑:我只谈论版本x64构build。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; namespace PropertyVsField { class Program { static int LEN = 20000000; static void Main(string[] args) { List<A> a = new List<A>(LEN); List<B> b = new List<B>(LEN); Random r = new Random(DateTime.Now.Millisecond); for (int i = 0; i < LEN; i++) { double p = r.NextDouble(); a.Add(new A() { P = p }); b.Add(new B() { P = p }); } Stopwatch sw = new Stopwatch(); double d = 0.0; sw.Restart(); for (int i = 0; i < LEN; i++) { d += a[i].P; } sw.Stop(); Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d); sw.Restart(); for (int i = 0; i < LEN; i++) { d += b[i].P; } sw.Stop(); Console.WriteLine(" field. {0}. {1}.", sw.ElapsedTicks, d); Console.ReadLine(); } } class A { public double P { get; set; } } class B { public double P; } } 

正如其他人已经提到的,吸气者是内联的

如果你想避免内联,你必须

  • 用手动replace自动属性:

     class A { private double p; public double P { get { return p; } set { p = value; } } } 
  • 并告诉编译器不要内联getter(或两者,如果你喜欢的话):

      [MethodImpl(MethodImplOptions.NoInlining)] get { return p; } 

请注意,第一个更改不会影响性能,而第二个更改会显示明确的方法调用开销:

手动属性:

 auto getter. 519005. 10000971,0237547. field. 514235. 20001942,0475098. 

没有内联的getter:

 auto getter. 785997. 10000476,0385552. field. 531552. 20000952,077111. 

看看属性VS领域 – 为什么这很重要? (Jonathan Aneja)在MSDN上的一个VB团队成员的博客文章。 他概述了财产与场的论点,并解释了一些微不足道的性质:

我听说在字段上使用字段的一个说法是“字段更快”,但是对于实际上不真实的普通属性,因为CLR的即时(JIT)编译器将内联属性访问并生成代码有效的直接访问字段。

JIT将内联任何内部度量标准确定的方法(而不仅仅是一个getter)。 鉴于一个标准的属性是return _Property; 它将在任何情况下内联。

您看到不同行为的原因是,在debugging模式下附带了一个debugging器,JIT会受到严重的阻碍,以确保任何堆栈位置与您期望的代码相匹配。

你也忘记了性能的头号规则,testing节奏思考。 例如,尽pipe快速sorting比插入sorting渐近快,但对于极小的input,插入sorting实际上更快。

唯一的解释可能是,CLR做了额外的优化(如果我在这里错了,就纠正我)。

是的,它被称为内联。 它是在编译器(机器码级 – 即JIT)中完成的。 由于getter / setter是微不足道的(即非常简单的代码),方法调用被破坏,getter / setter写在周围的代码中。

这在debugging模式下不会发生,以支持debugging(即在getter或setter中设置断点的能力)。

在visual studio中,在debugging器中没有办法做到这一点。 编译发行版,运行没有附加的debugging器,你会得到完整的优化。

我不相信在实际的应用中,这些属性以更复杂的方式被使用,它们将以相同的方式被优化。

世界充满了错误的幻想。 他们将被优化,因为他们仍然微不足道(即简单的代码,所以他们内联)。

应该指出的是,在Visual Studio中可以看到“真实”的性能。

  1. 在启用了Optimisations的情况下编译进发行模式。
  2. 转到“debugging” – >“选项和设置”,然后取消选中“在模块加载时禁止JIT优化(仅pipe理)”。
  3. 或者,取消选中“启用我的代码”,否则您可能无法join代码。

现在,即使在连接debugging器的情况下,接头组件也是一样的,如果您愿意的话,可以让您进入优化的拆卸过程。 这对了解CLR如何优化代码至关重要。