场与财产。 性能优化
请注意这个问题只与性能有关。 让我们跳过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中可以看到“真实”的性能。
- 在启用了Optimisations的情况下编译进发行模式。
- 转到“debugging” – >“选项和设置”,然后取消选中“在模块加载时禁止JIT优化(仅pipe理)”。
- 或者,取消选中“启用我的代码”,否则您可能无法join代码。
现在,即使在连接debugging器的情况下,接头组件也是一样的,如果您愿意的话,可以让您进入优化的拆卸过程。 这对了解CLR如何优化代码至关重要。