38

我在 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,我认为它解决了带有结构的方法(或结构的方法我不确定)没有内联的问题,所以不是这样。我想我至少在使用它,它肯定已经安装了,但话又说回来,我使用的是带有 SP1 的 Vista 64 位,它应该有 DX10.1,但我没有 DX10.1 ..

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

编辑:我很欣赏快速回答的家伙,但我表示我确实知道属性访问是一个方法调用,但我不知道为什么可能是内联方法比直接字段访问慢。

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

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

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

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

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

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

我对他的回答的回应:

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

谢谢大家!

4

5 回答 5

15

编辑2:

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

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

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


编辑3:

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

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

特别是,请查看第 2 点。


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

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

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

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

于 2009-03-11T00:26:57.337 回答
5

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

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

于 2009-03-11T00:27:56.523 回答
3
  • 公共字段是直接分配
  • 属性是方法,然后是更多代码,微不足道但更多。
于 2009-03-11T00:25:32.317 回答
3

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

于 2009-03-11T00:30:36.067 回答
3

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

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

于 2009-03-11T00:31:25.017 回答