属性比字段慢

14

似乎我看到的每篇文章都得出了同样的结论:仅返回字段的属性会被JIT内联,与字段几乎相同的性能。

然而,在我的当前情况下,似乎并非如此。我的程序进行了许多密集的计算,访问了许多仅仅是自动Getter和私有Setter的属性。但在这种特殊情况下,我只是复制一个对象。

启用优化模式,在发布模式下对代码进行分析,结果导致了对属性的get函数的许多调用。对Copy()的调用总共达到了约5.6毫秒。

属性基准测试

但是,当将属性转换为字段时,该函数运行的速度比使用属性时快了6倍:

输入图像说明

比较两个属性的相等性与使用字段相比似乎会带来更大的性能损失。以下是一个类的IEquatable实现的基准测试,使用相同的代码但使用字段替换属性。

输入图像说明

如果JIT应该通过内联优化属性,为什么会出现这种情况?我想保留属性,因为它们的访问控制很方便,但如果它们变得太慢,我就会坚持使用字段。

编辑:看起来受到此问题影响的一些(但不是全部)情况正在使用在接口中声明的属性。在这些情况下,没有使用其他多态性,但删除接口则在这些情况下使性能变化达到预期水平。

编辑2:如上一次编辑所述,似乎部分问题是由于接口虚拟调用引起的。经过更多调查,似乎在CLR中运行基准测试可以正确地内联属性,但即使勾选了“启用内联”,JetBrains dotTrace也不会内联。


这是“Release”还是“Debug”版本? - Hasan Emrah Süngü
发布,64位,带有优化的Dotnet Core 2.1 - Haus
我只是在本地进行简单的测试,如下所示 obj.Property = 5;obj.Field = 5; 在调试时,当我打开 Dissassembly 窗口时,我可以看到这两个调用是相同的。因此,我假设您的自动属性访问的对象有点大,因此它们没有被内联... 有许多内联规则,您可以检查其中一些 https://dev59.com/RW445IYBdhLWcg3w3N6z - Hasan Emrah Süngü
如果您多次访问同一属性,例如:var a = obj.PropertyA.Something(); var b = obj.PropertyA.Anotherthing(); var c = obj.PropertyA.OneAnotherthing();我建议将PropertyA存储到本地变量中,然后再调用方法等:var localProp = obj.PropertyA; - Hasan Emrah Süngü
在消费代码中,您是进行接口调用还是直接调用?如果是接口调用,请参考C#中“直接”虚拟调用与接口调用的性能比较 - dbc
这个回答解决了你的问题吗?为什么公共字段比属性更快? - Peter Duniho
1个回答

3

很遗憾,除了尝试通过使用更好的技术来帮助JITTER之外,你几乎不能做更多的事情。

[MethodImpl(MethodImplOptions.AggressiveInlining)]

AggressiveInlining:如果可能的话,该方法应该被内联。

从技术上讲,这只能用于方法或构造函数,但是似乎可以在getter和setter本身上测试它。也就是说,它编译通过(但我没有测试过)。

public int someType
{
   [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
   get;
   [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
   set;
}

注意:内联是一个神奇的黑盒子,抖动可能感觉到或者不感觉,即使属性只是建议。还要注意位数也可能影响内联。

最后,我认为你正在正确地进行操作,你应该使用基准测试器或分析工具,并进行微调优化。


哦,即使像这样手动建议内联也似乎没有帮助提高性能。有趣的是,似乎(在某些情况下,但不是所有情况)罪魁祸首是属性在类继承的接口中声明。没有抽象继承,只是实现接口。我猜JIT不会优化这些? - Haus
@Haus 关于接口,可能会有更多事情要做。我猜你只能使用字段,并根据性能结果进行直观的优化。我不确定是否有一种方法可以强制JIT在某些情况下进行内联。 - TheGeneral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接