2010-01-31 5 views
4

다음 코드로 인해 어려움을 겪고 있습니다. 중력장에서 움직이는 별 모델링에 사용되는 Forward-Euler algorithm의 F # 구현입니다.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 배 더 느립니다. 본문은 본문 유형의 구조체 배열입니다.

힘들다는 것은 고가의 함수이므로 대개 한 쌍씩 계산하면 Fij = -Fji라는 사실에 의존한다는 것입니다. 그러나 이것은 펼쳐지는 루프를 정말로 엉망으로 만듭니다.

감사의 말씀을드립니다! 없음이

감사합니다,

에이드 ... 숙제없는

업데이트 : 몸과 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; } 
} 

벡터는 표준 Vector 클래스에 대해 예상되는 연산자 종류를 정의합니다. 이 경우에는 .NET Framework에서 Vector3D 클래스를 사용할 수 있습니다. 실제로이 프레임 워크에 대한 조사가 진행 중입니다.

UPDATE 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) 항상 0이 아닌 경우에는 필요하지 않습니다. 이 최적화는 C# 코드에는 있지만 F # 버전에는 없었습니다 (doh!).

  • Math.Pow (x, -1.5)는 1/(Math.Sqrt (x) * x)보다 훨씬 느린 것 같습니다. 본질적으로이 알고리즘은 퍼포먼스가이 한 단계의 비용에 의해 결정된다는 점에서 약간 이상합니다.

  • 강제 계산을 인라인으로 이동하고 일부 나누기를 제거하면 성능이 약간 향상되지만 성능은 실제로 분기에 의해 종료되고 Sqrt의 비용에 의해 좌우됩니다. 구조체를 통해

WRT 사용하여 클래스 : 내가 관리되지 않는 코드로 또는 GPU에 몸의 배열을 얻을 필요가 경우 (CUDA와이 코드의 네이티브 C++ 구현과 DX9 렌더러)이 있습니다. 이러한 시나리오에서 메모리를 연속적으로 memcpy 할 수있는 것처럼 보입니다. 내가 신체 계급의 배열로부터 얻을 것 인 무엇인가.

답변

3

이 코드를 기능적 스타일로 다시 작성하는 것이 현명한 것인지 잘 모르겠습니다. 필자는 기능적 방식으로 쌍 상호 작용 계산을 작성하려는 몇 가지 시도를 보았으며 두 개 중첩 된 루프보다 하나씩 따르기가 더 어려웠습니다.

구조체와 클래스를 살펴보기 전에 (다른 사람이 이것에 대해 뭔가 똑똑하다고 생각합니다) 어쩌면 계산 자체를 최적화 할 수 있습니까?

당신은이 가속 델타를 계산하고,의는 다이 및 DAJ을 부르 자 :

다이 = R * M1 * m2/(RM은 *의 SQRT (RM))/(M1)

DAJ = R * (M1) * m2/(RM의 *의 SQRT (RM))/m2

[참고 : M1 = 체 [I] .mass, m2 = 체 [j]가 .mass..]

질량 제산 취소 다음과 같이 출력 :

dAi = r m2/(RM SQRT (RM))

DAJ = R (M1)/(RM SQRT (RM)) 이제

만 R/계산해야한다 (RM SQRT (RM))를 각 쌍 (i, j)을 생성한다. 1/(rm sqrt (rm)) = 1/(rm^1.5) = rm^-1.5이므로 r '= r * (rm ** -1.5)로하면 편집 : 불가능합니다. 조만간 최적화가 이루어지지 않습니다 (의견보기 참조). r '= 1.0/(r * sqrt r)을 계산하는 것이 가장 빠릅니다.

다이

귀하의 코드는 다음 뭔가 같은

member this.Integrate(dT, (bodies:Body[])) = 
    for i = 0 to bodies.Length - 1 do 
     for j = (i + 1) to bodies.Length - 1 do 
      let r = (b2.Position - b1.Position) 
      let rm = (float32)r.MagnitudeSquared + softeningLengthSquared    
      let r' = r * (rm ** -1.5) 
      bodies.[i].Acceleration <- bodies.[i].Acceleration + r' * bodies.[j].Mass 
      bodies.[j].Acceleration <- bodies.[j].Acceleration - r' * bodies.[i].Mass 
     bodies.[i].Position <- bodies.[i].Position + bodies.[i].Velocity * dT 
     bodies.[i].Velocity <- bodies.[i].Velocity + bodies.[i].Acceleration * dT 

봐, 엄마, 더 이상 분열 될 것 m2의 *의 R '

DAJ의 =의 M1 * R을'=!

경고 : 테스트되지 않은 코드. 자신의 책임하에 시도하십시오.

+0

감사합니다. 이제는 C#과 비슷한 성능의 F # 통합자를 얻었습니다. 그러나 (rm ** -1.5)는 상당한 성능 저하를 나타냅니다. ((float32) (Math.Sqrt ((float) rm)) * rm)이 빠를 것 같습니다. –

1

코드를 사용하여 arround를 재생하고 싶지만 Body 및 FloatVector의 정의가 없기 때문에 어렵습니다. 또한 사용자가 가리키는 원래 블로그 게시물에서 누락 된 것 같습니다.

나는 당신이 당신의 성능을 개선하고 F #의 게으른 계산을 사용하여 더 많은 기능 스타일로 다시 쓸 수있는 추측의 위험을 무릅 것 : http://msdn.microsoft.com/en-us/library/dd233247(VS.100).aspx

아이디어는 당신이 반복적으로 계산 될 수있는 비용 계산을 포장 매우 간단을 게으른 (...) 표현식을 사용하면 원하는만큼 여러 번 계산할 수 있으며 한 번 계산됩니다.

+0

감사합니다. Robert. 위의 내용이 올바른 길로 나를 안내 할 수도 있지만 더 많은 정보로 질문을 업데이트했습니다. 책을 좋아했습니다. –

+0

차가움. 사람들이 책을 읽는 것을 항상 듣기 좋습니다. – Robert

+0

아직이 게임을 할 기회가 없었지만 메모리 사용 공간에 영향을 줄 우려가 있습니다. 몸의 수는 일반적으로 10^4 또는 10^5 정도의 꽤 큽니다. 이것은 가변 배열을 사용하고 제 위치에서 수정하는 이유 중 하나입니다. 게으른 계산을 사용하면 (N^2)/2 배열을 효율적으로 저장할 수 있습니다. –