私はレイトレーサーの趣味のプロジェクトをやっていて、もともとはベクターオブジェクトとレイオブジェクトに構造体を使用していました。レイトレーサーはそれらを使用するのに最適な状況だと思いました。方法、彼らは軽量です。ただし、VectorとRayで「struct」を「class」に変更するだけで、パフォーマンスが大幅に向上しました。
何が得られますか?それらは両方とも小さく(ベクターの場合は3つのフロート、レイの場合は2つのベクター)、過度にコピーされません。もちろん、必要に応じてメソッドに渡しますが、それは避けられません。では、構造体を使用するときにパフォーマンスを低下させる一般的な落とし穴は何ですか?次のようなMSDNの記事を読みました。
この例を実行すると、構造体ループが桁違いに高速であることがわかります。ただし、ValueTypeをオブジェクトのように扱う場合は、ValueTypeの使用に注意することが重要です。これにより、プログラムに余分なボクシングとアンボクシングのオーバーヘッドが追加され、オブジェクトに固執した場合よりもコストがかかる可能性があります。これが実際に動作することを確認するには、上記のコードを変更して、fooとbarの配列を使用します。パフォーマンスはほぼ同等であることがわかります。
しかし、それはかなり古く(2001)、「それらを配列に入れると、ボクシング/アンボクシングが発生する」という全体が奇妙に感じました。本当?ただし、一次光線を事前に計算して配列に入れたので、この記事を取り上げて、必要なときに一次光線を計算し、配列に追加することはありませんでしたが、何も変わりませんでした。クラスでは、それでも1.5倍高速でした。
私は.NET3.5SP1を実行していますが、これにより、構造体メソッドがインライン化されなかった問題が修正されたと思います。
つまり、基本的に、ヒント、考慮すべきこと、避けるべきことはありますか?
編集:いくつかの回答で示唆されているように、私は構造体を参照として渡すことを試みたテストプロジェクトを設定しました。2つのベクトルを追加する方法:
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%高速になることに注意する必要があります。なぜこれが私のレイトレーサーと違うのかわかりません。