私は次のコードで苦労しています。これは、重力場で移動する星のモデリングに使用されるForward-EulerアルゴリズムのF#実装です。
let force (b1:Body) (b2:Body) =
let r = (b2.Position - b1.Position)
let rm = (float32)r.MagnitudeSquared + softeningLengthSquared
if (b1 = b2) then
VectorFloat.Zero
else
r * (b1.Mass * b2.Mass) / (Math.Sqrt((float)rm) * (float)rm)
member this.Integrate(dT, (bodies:Body[])) =
for i = 0 to bodies.Length - 1 do
for j = (i + 1) to bodies.Length - 1 do
let f = force bodies.[i] bodies.[j]
bodies.[i].Acceleration <- bodies.[i].Acceleration + (f / bodies.[i].Mass)
bodies.[j].Acceleration <- bodies.[j].Acceleration - (f / bodies.[j].Mass)
bodies.[i].Position <- bodies.[i].Position + bodies.[i].Velocity * dT
bodies.[i].Velocity <- bodies.[i].Velocity + bodies.[i].Acceleration * dT
これは機能しますが、正確には「機能的」ではありません。それはまたひどいパフォーマンスに苦しんでいます、それは同等のc#コードより2.5倍遅いです。bodyは、Bodyタイプの構造体の配列です。
私が苦労しているのは、force()は高価な関数であるため、通常はペアごとに1回計算し、Fij=-Fjiであるという事実に依存していることです。しかし、これは実際にループの展開などを台無しにします。
ありがたい提案をいただきました!いいえ、これは宿題ではありません...
ありがとう、
アデ
更新: BodyとVectorFloatを明確にするためにC#構造体として定義されています。これは、プログラムがF#/ C#とC ++/CLIの間で相互運用するためです。最終的にはBitBucketにコードを追加する予定ですが、これは進行中の作業であり、作成する前に解決する必要のある問題がいくつかあります。
[StructLayout(LayoutKind.Sequential)]
public struct Body
{
public VectorFloat Position;
public float Size;
public uint Color;
public VectorFloat Velocity;
public VectorFloat Acceleration;
'''
}
[StructLayout(LayoutKind.Sequential)]
public partial struct VectorFloat
{
public System.Single X { get; set; }
public System.Single Y { get; set; }
public System.Single Z { get; set; }
}
ベクトルは、標準のベクトルクラスに期待する演算子の種類を定義します。この場合、おそらく.NET FrameworkのVector3Dクラスを使用できます(私は実際にそれに切り替わることを調査しています)。
更新2:以下の最初の2つの応答に基づいてコードを改善しました。
for i = 0 to bodies.Length - 1 do
for j = (i + 1) to bodies.Length - 1 do
let r = ( bodies.[j].Position - bodies.[i].Position)
let rm = (float32)r.MagnitudeSquared + softeningLengthSquared
let f = r / (Math.Sqrt((float)rm) * (float)rm)
bodies.[i].Acceleration <- bodies.[i].Acceleration + (f * bodies.[j].Mass)
bodies.[j].Acceleration <- bodies.[j].Acceleration - (f * bodies.[i].Mass)
bodies.[i].Position <- bodies.[i].Position + bodies.[i].Velocity * dT
bodies.[i].Velocity <- bodies.[i].Velocity + bodies.[i].Acceleration * dT
b1 == b2の場合をカバーするフォース関数の分岐は、最悪の違反者です。softeningLengthが非常に小さい場合でも(Epsilon)、常にゼロ以外の場合は、これは必要ありません。この最適化はC#コードに含まれていましたが、F#バージョンには含まれていませんでした(doh!)。
Math.Pow(x、-1.5)は、1 /(Math.Sqrt(x)* x)よりもはるかに遅いようです。基本的に、このアルゴリズムは、パフォーマンスがこの1つのステップのコストによって決定されるという点で少し奇妙です。
力の計算をインラインに移動し、いくつかの除算を取り除くこともある程度の改善をもたらしますが、パフォーマンスは実際には分岐によって損なわれ、Sqrtのコストによって支配されていました。
構造体よりもクラスを使用するWRT:ボディの配列をアンマネージコードまたはGPUに取り込む必要がある場合(このコードのCUDAおよびネイティブC ++実装とDX9レンダラー)があります。これらのシナリオでは、メモリの連続したブロックをmemcpyできることは、進むべき道のように思えます。Bodyクラスの配列から得られるものではありません。