我正在做一个光线追踪器爱好项目,最初我使用结构体作为我的 Vector 和 Ray 对象,我认为光线追踪器是使用它们的完美情况:你创建了数百万个它们,它们的寿命不会超过一个方法,它们是轻量级的。然而,通过简单地将 Vector 和 Ray 上的“struct”更改为“class”,我获得了非常显着的性能提升。
是什么赋予了?它们都很小(向量 3 个浮点数,射线 2 个向量),不要过度复制。当然,我确实会在需要时将它们传递给方法,但这是不可避免的。那么,在使用结构时会导致性能下降的常见陷阱有哪些?我读过这篇MSDN 文章,内容如下:
当您运行此示例时,您会发现结构循环的速度要快几个数量级。但是,当您将它们视为对象时,请注意使用 ValueTypes 是很重要的。这会为您的程序增加额外的装箱和拆箱开销,并且最终可能会比您卡在对象上的成本更高!要查看实际情况,请修改上面的代码以使用 foos 和 bar 数组。您会发现性能或多或少相等。
然而,它已经很老了(2001 年),整个“把它们放在一个数组中导致装箱/拆箱”让我觉得很奇怪。真的吗?但是,我确实预先计算了主光线并将它们放在一个数组中,所以我接受了这篇文章并在需要时计算了主光线,并且从未将它们添加到数组中,但它并没有改变任何东西:类,它仍然快 1.5 倍。
我正在运行 .NET 3.5 SP1,我相信它解决了 struct 方法从未内联的问题,因此也不可能是这样。
所以基本上:任何提示,要考虑的事情以及要避免什么?
编辑:正如一些答案中所建议的,我已经建立了一个测试项目,我尝试将结构作为参考传递。添加两个向量的方法:
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 优化为传递此结构的最佳方式?
EDIT2:我必须注意,在我的测试项目中使用结构比使用类快大约 50%。为什么这对我的光线追踪器有所不同,我不知道。