我一直在努力使用以下代码。它是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() 是一个昂贵的函数,所以通常你为每对计算一次并依赖 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 框架中的 Vector3D 类(我实际上正在研究切换到它)。
更新 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
force 函数中覆盖 b1 == b2 情况的分支是最严重的违规者。如果 softeningLength 始终非零,即使它非常小(Epsilon),您也不需要它。此优化在 C# 代码中,但不在 F# 版本中(doh!)。
Math.Pow(x, -1.5) 似乎比 1/ (Math.Sqrt(x) * x) 慢很多。本质上,这个算法有点奇怪,因为它的性能取决于这一步的成本。
将力计算内联并消除一些分歧也带来了一些改进,但性能确实被分支杀死并且由 Sqrt 的成本主导。
WRT 在结构上使用类:在某些情况下(此代码的 CUDA 和本机 C++ 实现以及 DX9 渲染器),我需要将主体数组放入非托管代码或 GPU 上。在这些情况下,能够 memcpy 一个连续的内存块似乎是要走的路。不是我从一组 Body 中得到的东西。