C# 公共字段是否正常?

C# 公共字段是否正常?,c#,.net,performance,optimization,field,C#,.net,Performance,Optimization,Field,在你从直觉做出反应之前,就像我最初做的那样,请阅读整个问题。我知道它们让你觉得脏兮兮,我知道我们以前都被烧死过,我知道这不是“好风格”,但是,公共场地还可以吗 我正在开发一个相当大规模的工程应用程序,它创建并使用一个结构的内存模型(从高层建筑到桥梁到棚屋,都不重要)。本项目涉及大量的几何分析和计算。为了支持这一点,模型由许多微小的不可变只读结构组成,以表示点、线段等。在典型的程序执行过程中,这些结构的一些值(如点的坐标)会被访问数千万次或数亿次。由于模型的复杂性和计算量,性能是绝对关键的 我觉得

在你从直觉做出反应之前,就像我最初做的那样,请阅读整个问题。我知道它们让你觉得脏兮兮,我知道我们以前都被烧死过,我知道这不是“好风格”,但是,公共场地还可以吗

我正在开发一个相当大规模的工程应用程序,它创建并使用一个结构的内存模型(从高层建筑到桥梁到棚屋,都不重要)。本项目涉及大量的几何分析和计算。为了支持这一点,模型由许多微小的不可变只读结构组成,以表示点、线段等。在典型的程序执行过程中,这些结构的一些值(如点的坐标)会被访问数千万次或数亿次。由于模型的复杂性和计算量,性能是绝对关键的

我觉得我们正在尽我们所能优化我们的算法、性能测试以确定瓶颈、使用正确的数据结构等等。我不认为这是过早优化的情况。性能测试显示数量级(至少)当直接访问字段而不是通过对象上的属性访问字段时,性能提升。有了这些信息,我们还可以公开与属性相同的信息,以支持数据绑定和其他情况。。。这样行吗?记住,不可变结构上的只读字段。谁能想出一个让我后悔的理由

下面是一个示例测试应用程序:


结构点{
公共点(双x、双y、双z){
_x=x;
_y=y;
_z=z;
}
公共只读双U x;
公共只读双字节;
公共只读双精度;
公共双X{get{return\ux;}
公共双Y{get{return}
公共双Z{get{return}
}
班级计划{
静态void Main(字符串[]参数){
const int loopCount=10000000;
var点=新点(12.0,123.5,0.123);
var sw=新秒表();
双x,y,z;
双重计算值;
sw.Start();
for(int i=0;i

结果:
直接现场访问:3262
物业使用权:24248
总差异:20986
平均差:0.00020986



这仅仅是21秒,但为什么不呢?

鉴于您处理的是带有只读字段的不可变对象,我想说您遇到了一种情况,即我不认为公共字段是一种肮脏的习惯。

我知道您这样做有点肮脏,但是,当性能成为一个问题时,规则和指导方针会陷入地狱,这并不罕见。例如,许多使用MySQL的高流量网站都有数据复制和非规范化的表。其他的

这个故事的寓意——它可能与你所学到或建议的一切背道而驰,但基准并不存在。如果效果更好,就去做吧

IMO,“无公共字段”规则是技术上正确的规则之一,但除非您设计的库是供公众使用的,否则如果您违反它,就不太可能给您带来任何问题

在我被大量否决之前,我应该补充一点,封装是一件好事。鉴于不变量“如果HasValue为false,则Value属性必须为null”,此设计存在缺陷:

class A {
    public bool HasValue;
    public object Value;
}
然而,考虑到这种不变性,这种设计同样存在缺陷:

class A {
    public bool HasValue { get; set; }
    public object Value { get; set; }
}
正确的设计是正确的

class A {
    public bool HasValue { get; private set; }
    public object Value { get; private set; }

    public void SetValue(bool hasValue, object value) {
        if (!hasValue && value != null)
            throw new ArgumentException();
        this.HasValue = hasValue;
        this.Value    = value;
    }
}

(最好是提供一个初始化构造函数并使类不可变)。

如果您真的需要额外的性能,那么这可能是正确的做法。如果您不需要额外的性能,那么可能不需要

Rico Mariani有几个相关的帖子:


    • 以下是一些可以使用的场景(摘自《框架设计指南》一书):

      • 对常量使用常量字段吗 这永远不会改变
      • 不要使用公共电话 预定义字段的静态只读字段 对象实例
      如果不是:

      • 不要分配可变的实例 将类型设置为只读字段

      从你所说的,我不明白为什么你的琐碎的属性没有被JIT所内衬?

      < P>个人,我唯一考虑使用公共字段的时间是在一个非常具体的实现专用的私有嵌套类中。

      其他时候,这样做感觉太“错误”了


      CLR将通过优化方法/属性(在发布版本中)来保证性能,因此这不应该成为问题。

      尝试编译发布版本并直接从exe运行,而不是通过调试器运行。如果应用程序是通过调试器运行的,那么JIT编译器将不会内联属性访问器。我无法复制你的结果。事实上,我运行的每个测试都表明执行时间几乎没有差异

      但是,就像
      class A {
          public bool HasValue { get; private set; }
          public object Value { get; private set; }
      
          public void SetValue(bool hasValue, object value) {
              if (!hasValue && value != null)
                  throw new ArgumentException();
              this.HasValue = hasValue;
              this.Value    = value;
          }
      }
      
              sw.Start();
              for (int i = 0; i < loopCount; i++)
              {
                  x = point._x;
                  y = point._y;
                  z = point._z;
                  calculatedValue = x * y / z;
              }
              sw.Stop();
              double fieldTime = sw.ElapsedMilliseconds;
              Console.WriteLine("Direct field access: " + fieldTime);
      
              sw.Reset();
              sw.Start();
              for (int i = 0; i < loopCount; i++)
              {
                  x = point.X;
                  y = point.Y;
                  z = point.Z;
                  calculatedValue = x * y / z;
              }
              sw.Stop();
      
      for (int i = 0; i < loopCount; i++)
      00000025  xor         eax,eax 
      00000027  inc         eax  
      00000028  cmp         eax,989680h 
      0000002d  jl          00000027 
      }
      
      for (int i = 0; i < loopCount; i++)
      00000094  xor         eax,eax 
      00000096  fld         dword ptr ds:[01300210h] 
      0000009c  fdiv        qword ptr ds:[01300218h] 
      000000a2  fstp        st(0) 
      000000a4  inc         eax  
      000000a5  cmp         eax,989680h 
      000000aa  jl          00000096 
      }
      
      Point point = new Point(12.0, 123.5, 0.123);
      
      Random r = new Random();
      Point point = new Point(r.NextDouble(), r.NextDouble(), r.NextDouble());
      
      Direct field access: 133
      Property access: 133
      Total difference: 0
      Average difference: 0
      
      000000dd  xor         eax,eax 
      000000df  fld         qword ptr [esp+20h] 
      000000e3  fmul        qword ptr [esp+28h] 
      000000e7  fdiv        qword ptr [esp+30h] 
      000000eb  fstp        st(0) 
      000000ed  inc         eax  
      000000ee  cmp         eax,989680h 
      000000f3  jl          000000DF (This loop address is the only difference)