为啥公共领域比属性快?

Posted

技术标签:

【中文标题】为啥公共领域比属性快?【英文标题】:Why are public fields faster than properties?为什么公共领域比属性快? 【发布时间】:2010-10-12 13:57:26 【问题描述】:

我在 XNA 中四处寻找,发现其中的 Vector3 类使用公共字段而不是属性。我尝试了一个快速基准测试,发现对于struct,差异非常显着(将两个向量加在一起 ​​1 亿次需要 2.0 秒的属性和 1.4 秒的字段)。对于引用类型,差异似乎并没有那么大,但确实存在。

那是为什么呢?我知道一个属性被编译成get_Xset_X 方法,这会产生方法调用开销。但是,这些简单的 getter/setter 总是 不会被 JIT 内联吗?我知道你不能保证 JIT 决定做什么,但肯定这在概率列表中相当高吗?还有什么可以将公共字段与机器级别的属性分开?

我一直想知道的一件事是:自动实现的属性 (public int Foo get; set; ) 如何比公共字段“更好”的 OO 设计?或者更好地说:这两个不同有什么不同?我知道通过反射使它成为一个属性更容易,但还有什么?我敢打赌这两个问题的答案是一样的。

顺便说一句:我正在使用 .NET 3.5 SP1,我相信它解决了带有结构的方法(或方法 of 结构,我不确定)没有内联的问题,所以这是不是。我想我至少在使用它,它肯定已经安装了,但话又说回来,我使用的是带有 SP1 的 Vista 64 位,它应该有 DX10.1,但我没有 DX10.1 ..

另外:是的,我一直在运行发布版本 :)

编辑:感谢大家的快速回答,但我表示我确实知道属性访问是一种方法调用,但我不知道为什么大概,内联方法比直接字段访问慢。

编辑 2:所以我创建了另一个 struct,它使用了明确的 GetX() 方法(我怎么不会错过我的 Java 日子)无论我是否禁用内联(通过[MethodImplAttribute(MethodImplOptions.NoInlining)])都执行相同的操作,所以结论是:非静态方法显然永远不会内联,甚至在结构上也不会。

我认为存在例外情况,JIT 可以优化虚拟方法调用。为什么这种情况不会发生在不知道继承的结构上,因此方法调用只能指向一个可能的方法,对吧?还是因为你可以在上面实现一个接口?

这有点可惜,因为它真的会让我考虑在性能关键的东西上使用属性,但是使用字段让我觉得很脏,我还不如用 C 来写我正在做的事情。

编辑 3:我发现 this 发布了完全相同的主题。他的最终结论是属性调用确实得到了优化。我也可以发誓,我已经读过很多次简单的 getter/setter 属性将被内联,尽管在 IL 中是 callvirt。那我是不是要疯了?

编辑 4:Reed Copsey 在下面的评论中发布了答案:

回复:Edit3 - 查看我更新的评论:我相信这是 x86 JIT 与 x64 JIT 的问题。 x64 中的 JIT 并不成熟。随着越来越多的 64 位系统每天上线,我希望 MS 能够迅速改进这一点。 ——里德·科普西

我对他的回答的回应:

谢谢,这就是答案!我尝试强制 x86 构建,所有方法都同样快,而且比 x64 快得多。实际上,这对我来说非常令人震惊,我不知道我生活在我的 64 位操作系统上的石器时代。我会在我的回答中包含您的评论,这样它会更好。 – 朱利安R

谢谢大家!

【问题讨论】:

问题:如果该字段是公共的但也有一个属性会发生什么。那么它是内联的吗? 回复:Edit3 - 查看我更新的评论:我相信这是 x86 JIT 与 x64 JIT 的问题。 x64 中的 JIT 并不成熟。随着每天都有更多的 64 位系统上线,我希望 MS 能够迅速改进这一点。 谢谢,这就是答案!我尝试强制 x86 构建,所有方法都同样快,而且比 x64 快得多。实际上,这对我来说非常令人震惊,我不知道我在 64 位操作系统上生活在石器时代。我会将您的评论包含在我的答案中,以便更好地脱颖而出。 我在下面有一个答案 - 还为您提供了有关其他相关主题的更多信息 :) 顺便说一句 - 这启发了我问:***.com/questions/635019/… 【参考方案1】:

编辑 2:

我在这里有另一个潜在的想法:

您提到您在 x64 上运行。我已经在 x86 上测试了同样的问题,并且在使用自动属性与字段时看到了相同的性能。但是,如果您查看 Connect 和邮件列表/论坛帖子,网上有很多参考资料表明 x64 CLR 的 JIT 是不同的代码库,并且与 x86 JIT 具有非常不同的性能特征。我猜这是 x64 仍然落后的地方。

另外,仅供参考,在 .net 3.5sp1 中修复的结构/方法/等内容是在 x86 方面,事实上,将结构作为参数的方法调用永远不会在 .net3 之前的 x86 上内联。 5sp1。这与您系统上的讨论几乎无关。


编辑 3:

另一件事:至于为什么 XNA 使用字段。我实际上是在他们宣布 XNA 的游戏节上。 Rico Mariani 发表了一次演讲,他在博客中提出了许多相同的观点。似乎 XNA 的人在开发一些核心对象时也有类似的想法。见:

http://blogs.msdn.com/ricom/archive/2006/09/07/745085.aspx

特别是,请查看第 2 点。


至于为什么自动属性优于公共字段:

它们允许您更改类的 v2 中的实现,并根据需要将逻辑添加到属性获取/设置例程中,而无需更改您与最终用户的接口。随着时间的推移,这会对您维护库和代码的能力产生深远的影响。

----来自原始帖子-但发现这不是问题--------

您是否在 VS 之外运行发布版本?这可能是为什么事情没有得到优化的一种解释。通常,如果您在 VS 中运行,即使是优化的发布版本,VS 主机进程也会禁用 JIT 的许多功能。这可能会导致性能基准发生变化。

【讨论】:

是的,我尝试关闭所有 VS 实例并在 Release 文件夹中运行 .exe,但结果相同(尽管两个版本在 VS 之外稍快)。是的,这是使用属性的一个很好的理由,即使你一开始没有填写 get/set :) 结构类型的最佳用例是将一些变量与胶带绑定在一起(例如点的坐标)。如果一个结构应该接受任何一对整数并将它们报告为 X 和 Y,那么实际上没有任何有用的行为,该类型的未来版本可能会添加到 X 和 Y 属性访问器而不破坏事物。最好让一个结构说出它是什么(一个 X 字段和一个 Y 字段),而不是让它“保密”。【参考方案2】:

访问字段只是内存引用,而使用属性实际上是调用方法并且包括函数调用开销。使用属性而不是字段的原因是为了使您的代码免受更改并提供更好的访问粒度。通过不直接公开您的字段,您可以更好地控制访问的完成方式。使用自动字段可以让您获得典型的 getter/setter 行为,但构建了更改此行为的能力,而无需随后将更改传播到代码的其他部分。

例如,假设您要更改代码,以便对字段的访问由当前用户的角色控制。如果您公开了该字段,则必须触摸访问它的代码的每个部分。通过属性公开它允许您修改属性代码以添加新需求但不会导致对访问它的任何代码进行不必要的更改

【讨论】:

【参考方案3】:

XNA 必须以 Xbox 360 为目标,而 .NET Compact Framework 中的 JIT 并不像其桌面版那样复杂。 .NET CF JIT'er 不会内联属性方法。

【讨论】:

【参考方案4】:

您应该阅读 Vance 的这篇文章。它详细说明了为什么 JIT'er 并不总是内联方法,即使看起来很明显它们应该是内联。

http://blogs.msdn.com/vancem/archive/2008/08/19/to-inline-or-not-to-inline-that-is-the-question.aspx

【讨论】:

这是一篇探讨内联成本的论文:运行时和编译成本(关注 GC):cs.anu.edu.au/~Steve.Blackburn/pubs/papers/…【参考方案5】: 公共字段是直接赋值 属性是方法,然后是更多代码,微不足道但更多。

【讨论】:

是的,正如我所说的,但是当内联 getter/setter 方法时,不是也可以直接访问属性的支持字段吗? 朱利安 - 是的,应该。不过,你是在 VS 中运行的吗?如果是这样,VS 的宿主项目会禁用 JIT 的许多优化,即使它是发布版本。请参阅下面的评论..

以上是关于为啥公共领域比属性快?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Vec::len 是方法而不是公共属性?

JS 语义:为啥私有和公共类属性似乎重新排序?

如果wen可以定义公共属性,Angular2为啥要使用getter

为啥 C# 公共静态变量不需要实例化?

css公共样式能更高效,为啥不抽公共样式呢?

Unity 脚本中公共变量初始化值修改后,为啥Inpector面板的对应变量属性不更新?