何时构建答案?

我正在做一个光线追踪器爱好项目,最初我使用的是用于我的Vector和Ray对象的结构体,我认为光线追踪器是使用它们的最佳状态:您创建了数百万个,它们的寿命不会超过一个方法,它们是轻量级的。 但是,通过简单地将Vector和Ray上的'struct'更改为'class',我获得了非常显着的性能提升。

是什么赋予了? 它们都很小(Vector为3个浮点,Ray为2个),不要过度复制。 当然,我会在需要时将它们传递给方法,但这是不可避免的。 那么在使用结构时会出现哪些常见的陷阱会导致性能下降? 我读过这篇MSDN文章,内容如下:

当你运行这个例子时,你会看到结构循环速度快了几个数量级。 但是,将对象当作对象使用时,请注意使用ValueTypes。 这会给你的程序增加额外的装箱和取消装箱开销,并且最终会让你花费更多的钱,如果你坚持使用物件! 为了看到这一点,修改上面的代码以使用一个foos和bar数组。 你会发现性能差不多一样。

然而,这是相当古老的(2001年),整个“把它们放在阵列中导致拳击/拆箱”让我觉得很奇怪。 真的吗? 但是,我已经预先计算了初级光线并将它们放入一个数组中,因此我在本文中采用了这种方法,并在需要时计算了初级光线,并且从未将它们添加到数组中,但它没有改变任何内容:类,它仍然快1.5倍。

我正在运行.NET 3.5 SP1,我相信它解决了一个问题,即结构方法从来没有内联过,因此也不能这样做。

所以基本上:任何提示,要考虑的事情和要避免的事情?

编辑:正如在一些答案建议,我已经建立了一个测试项目,我已经尝试通过结构作为参考。 添加两个向量的方法:

public static VectorStruct Add(VectorStruct v1, VectorStruct v2)
{
  return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

public static VectorStruct Add(ref VectorStruct v1, ref VectorStruct v2)
{
  return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

public static void Add(ref VectorStruct v1, ref VectorStruct v2, out VectorStruct v3)
{
  v3 = new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

对于每个我有以下基准方法的变化:

VectorStruct StructTest()
{
  Stopwatch sw = new Stopwatch();
  sw.Start();
  var v2 = new VectorStruct(0, 0, 0);
  for (int i = 0; i < 100000000; i++)
  {
    var v0 = new VectorStruct(i, i, i);
    var v1 = new VectorStruct(i, i, i);
    v2 = VectorStruct.Add(ref v0, ref v1);
  }
  sw.Stop();
  Console.WriteLine(sw.Elapsed.ToString());
  return v2; // To make sure v2 doesn't get optimized away because it's unused. 
}

所有似乎都表现非常相似。 是否有可能通过JIT将其优化为传递此结构的最佳方式?

编辑2:我必须注意,在我的测试项目中使用结构比使用类快50%。 为什么这是我不知道的我的光线跟踪器不同。


基本上,不要让它们太大,并尽可能地通过ref传递它们。 我发现这完全相同的方式...通过将我的Vector和Ray类更改为结构。

随着越来越多的内存被传递,它肯定会导致缓存抖动。


一个结构数组可以是内存中的单个连续结构,而对象数组中的项(引用类型的实例)需要通过指针(即对垃圾收集堆上的对象的引用)单独寻址。 因此,如果你同时处理大量的项目,结构会给你一个性能提升,因为它们需要更少的间接指向。 另外,结构体不能被继承,这可能允许编译器进行额外的优化(但这只是一种可能性,取决于编译器)。

但是,结构具有完全不同的赋值语义,也不能被继承。 因此,除了出于需要的性能原因外,我通常会避免使用结构。


结构

内存中由结构(值类型)编码的值v数组看起来像这样:

VVVV

由类(引用类型)编码的值v的数组看起来像这样:

PPPP

..v..v ...... VV。

其中p是这些指针或引用,它们指向堆上的实际值v。 点表示可能散布在堆上的其他物体。 在引用类型的情况下,您需要通过相应的p引用v,对于值类型,您可以直接通过数组中的偏移量获取值。


在关于何时使用结构的建议中,它指出它不应该大于16个字节。 你的向量是12个字节,接近极限。 Ray有两个向量,将它放在24个字节处,这明显超过了建议的限制。

当一个结构体大于16个字节时,它不能再用一组指令有效地复制,而是使用一个循环。 所以,通过传递这个“魔术”的限制,当你传递一个结构时,你实际上做了很多工作,而不是传递一个对象的引用。 这就是为什么尽管分配对象时会有更多开销,但代码在类事件中速度更快。

Vector可能仍然是一个结构,但是Ray太大而不能像结构一样工作。

链接地址: http://www.djcxy.com/p/28053.html

上一篇: When are structs the answer?

下一篇: Why is it okay that this struct is mutable? When are mutable structs acceptable?