C#9、.NET 5.0字段的速度比版本中的属性快

C#9、.NET 5.0字段的速度比版本中的属性快,c#,performance,properties,field,C#,Performance,Properties,Field,我有一个不可变的结构,用于3D向量。我知道基本的getter属性是(应该是)内联的,因此应该执行与字段相同的操作,假设您处于版本配置中,并且在VS之外运行,而不受调试或JIT抑制的任何影响。然而,这不是我所看到的行为,我正试图找出原因。(顺便说一句,我已经阅读了关于这个的所有其他帖子,所以我发现没有用) 设置:VS2019 v16.8.4,使用.NET 5.0和C#9。发布配置,在所有项目中启用优化 首先,使用公共字段的向量类的相关部分(用于比较): 以及控制台应用程序代码,用于测试从100万个

我有一个不可变的结构,用于3D向量。我知道基本的getter属性是(应该是)内联的,因此应该执行与字段相同的操作,假设您处于版本配置中,并且在VS之外运行,而不受调试或JIT抑制的任何影响。然而,这不是我所看到的行为,我正试图找出原因。(顺便说一句,我已经阅读了关于这个的所有其他帖子,所以我发现没有用)

设置:VS2019 v16.8.4,使用.NET 5.0和C#9。发布配置,在所有项目中启用优化

首先,使用公共字段的向量类的相关部分(用于比较):

以及控制台应用程序代码,用于测试从100万个向量访问字段的时间。(我知道我可以做一些事情来改进基准代码,例如提高线程优先级或预热函数,但这里的关键是我在使用字段和属性时看到的性能差异——这是一致的):

Main()
的唯一更改是:

val = vectors[i].X; //Now access via a property instead of the field.
(也就是说,通过auto-prop而不是字段进行访问)然后,当我在release config中构建此文件并在命令提示符下运行可执行文件数次时,我会看到15000个刻度范围内的时间,或者大约慢5倍

同样,我已经确认了这两个测试,我在版本配置中编译,在我所有的项目中都检查了优化,并且我在Studio外部的命令提示符中运行可执行文件(因此不可能附加调试器)


我错过了什么?为什么访问酒店的时间要长5倍?

我没有看到您的结果

根据我的CPU和环境,结果都在典型的余量或误差范围内

注意,对于此类测试,您应该始终使用基准测试工具,否则会出现很多问题。在本例中,我使用的是BenchmarkDotnet

配置

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.746 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.102
  [Host]        : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT
  .NET Core 5.0 : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT

Job=.NET Core 5.0  Runtime=.NET Core 5.0
结果

方法 卑鄙 错误 标准偏差 领域 1.535毫秒 0.0307毫秒 0.0605毫秒 道具 1.512毫秒 0.0204毫秒 0.0171毫秒 丙氨酸 1.567毫秒 0.0295毫秒 0.0404毫秒
属性访问可能需要更长的时间,因为属性访问不是内联的。您可以在-中使用代码,您可以看到没有内联发生

另外,请确保不测量可执行文件的第一次运行。在第一次执行时,没有jitted代码,因此没有内联代码。你每次跑步都看到相同的数字吗


当然,编译可以内联您的属性。在您的代码库中,在某些特定的上下文中,编译器可能决定不内联您的属性。

因此我对您从何处获得这些信息感兴趣“我知道基本getter属性是(应该是)”Jon Skeet()提到简单属性通常是内联的。尽管有趣的是,他确实提到了双打有时性能较慢。(这篇文章已经有10多年的历史了,所以不确定它是否仍然适用)。另外,我发现的所有搜索结果都表明简单属性通常是内联的。5倍的差异是显著的!刚刚发现MSDN文档也这样说(请参阅):“例如,当您从get访问器返回私有变量并启用优化时,对get访问器方法的调用由编译器内联,因此没有方法调用开销。”我在BenchmarkDotnetts上看不到您的结果(感谢您花时间进行测试并帮助确认我正在失去理智:)。现在我只需要找出为什么我看不到这个。我将尝试您发布的内容(在新的解决方案中)。他的示例使用
+=
您的示例不使用。当访问某个字段时,可能乐观者已经注意到您正在丢弃结果。但是要证明一个属性getter没有副作用要困难得多。@JeremyLakeman我用的是
+=
,但这是一个有意识的决定。只需使用
=
进行基准测试即可compare@JeremyLakeman只是用Ops的实际代码进行了基准测试,没有任何变化。我想在框架或CPU架构上可能会有所不同,尽管我没有访问AMD@ToddBurch在Ryzen3800x(所有核心OC到4.4)上运行上述代码可以得到预期的结果。两次支柱运行的平均值为1.075 ms,现场平均值为1.052 ms。在预期差异范围内。只是想帮助消除CPU架构。配置相同,减去核心差异。
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct Vector3
{
  public double X { get; }
  public double Y { get; }
  public double Z { get; }
  ...
}
val = vectors[i].X; //Now access via a property instead of the field.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.746 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.102
  [Host]        : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT
  .NET Core 5.0 : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT

Job=.NET Core 5.0  Runtime=.NET Core 5.0