我在XNA中闲逛并看到其中的Vector3
类使用公共字段而不是属性。我尝试了一个快速的基准测试,发现,对于struct
,差异非常显着(将两个向量组合在一起,1亿次带有2.0s属性和1.4s带字段)。对于参考类型,差异似乎并不大,但它存在。
那为什么呢?我知道属性被编译成get_X
和set_X
方法,这会产生方法调用开销。但是,这些简单的getter / setter 是否总是被JIT内联?我知道你无法保证JIT决定做什么,但肯定这在概率列表中相当高?还有什么将公共领域与机器级别的财产分开?
有一点我一直想知道:自动实现的属性(public int Foo { get; set; }
)如何比公共领域更好的'OO设计?或者更好地说:那两个不同的怎么样?我知道用反射把它变成一个属性更容易,但其他什么呢?我打赌这两个问题的答案都是一样的。
另外:是的,我一直在运行发布版本:)
编辑:我很欣赏快速回答的人,但我表示我做知道属性访问是方法调用,但我不知道为什么据推测,内联方法比直接字段访问慢。
编辑2 :所以我创建了另一个使用显式GetX()方法的struct
(o我怎么不错过我的Java日 at all )并且无论我是否禁用内嵌(通过[MethodImplAttribute(MethodImplOptions.NoInlining)]
),都执行相同的操作,因此结论:非静态方法显然从未内联,甚至不在结构上。
我认为有异常,JIT可以选择虚拟方法调用。为什么不能在没有继承的结构上发生这种情况,因此方法调用只能指向一种可能的方法,对吧?或者是因为你可以在其上实现一个接口?
这真是一种耻辱,因为它会让我考虑在性能关键的东西上使用属性,但使用字段让我觉得很脏,我不妨写下我在C中做的事情。
编辑3 :我发现this发布了完全相同的主题。他的最终结论是,财产电话确实被优化了。我也可以发誓我已经多次阅读过简单的getter / setter属性将被内联,尽管在IL中是callvirt
。我疯了吗?
编辑4 :Reed Copsey在下面的评论中发布了答案:
Re:Edit3 - 请参阅我的更新评论:我相信这是x86 JIT vs x64 JIT问题。 x64中的JIT不够成熟。随着越来越多的64位系统每天上线,我希望MS能够迅速提高。 - 里德·科普西
我回答他的回答:
谢谢,这就是答案!我尝试强制x86构建,所有方法都同样快,并且比x64快得多。实际上这对我来说非常令人震惊,我不知道我是在64位操作系统上生活在石器时代。我会在你的答案中包含你的评论,以便更好。 - JulianR
谢谢大家!
答案 0 :(得分:15)
编辑2:
我在这里有另一个潜在的想法:
您提到您在x64上运行。我在x86上测试了同样的问题,并且在使用自动属性与字段时看到了相同的性能。但是,如果你浏览一下Connect和邮件列表/论坛帖子,有许多在线参考资料,x64 CLR的JIT是一个不同的代码库,并且与x86 JIT具有非常不同的性能特征。我猜这是x64仍然落后的地方。
另外,FYI,在.net 3.5sp1中修复的struct / method / etc事物是在x86端,并且事实上,将结构作为参数的方法调用永远不会在.net3之前的x86上内联。 5sp1。这与您对系统的讨论无关。
编辑3:
另一件事:至于为什么XNA使用字段。我实际上是在游戏巨星,他们宣布XNA。 Rico Mariani发表了一篇演讲,他提到了许多与博客相同的观点。似乎XNA人员在开发一些核心对象时有类似的想法。参见:
http://blogs.msdn.com/ricom/archive/2006/09/07/745085.aspx
特别是,请查看第2点。
至于为什么自动属性优于公共领域:
它们允许您更改类的v2中的实现,并根据需要将逻辑添加到属性get / set例程中,而无需更改最终用户的界面。这会对您维护库和代码的能力产生深远的影响。
----从原帖 - 但发现这不是问题--------
您是否在VS的之外运行发布版本?这可以解释为什么事情没有得到优化。通常,如果您在VS中运行,即使是优化的发布版本,VS主机进程也会禁用JIT的许多功能。这可能会导致性能基准发生变化。
答案 1 :(得分:5)
你应该阅读万斯的这篇文章。它详细说明了为什么方法并不总是由JIT'er内联,即使它看起来应该是非常明显的。
http://blogs.msdn.com/vancem/archive/2008/08/19/to-inline-or-not-to-inline-that-is-the-question.aspx
答案 2 :(得分:3)
答案 3 :(得分:3)
XNA必须以XBox 360为目标,而.NET Compact Framework中的JIT并不像桌面版本那样复杂。 .NET CF JIT'er不会内联属性方法。
答案 4 :(得分:3)
访问字段只是一个内存引用,而使用属性实际上是调用方法并包含函数调用开销。使用属性而不是字段的原因是为了使代码与更改隔离,并提供更好的访问粒度。通过不直接暴露您的领域,您可以更好地控制访问的方式。使用自动字段可以获得典型的getter / setter行为,但是可以在不需要将更改传播到代码的其他部分的情况下进行更改。
例如,假设您要更改代码,以便对当前用户的角色控制对字段的访问。如果您公开暴露了该字段,则必须触摸访问该字段的代码的每个部分。通过属性公开它允许您修改属性代码以添加新要求但不会导致对访问它的任何代码进行不必要的更改。