멀티 스레딩 2D 중력 계산


24

우주 탐사 게임을 만들고 있는데 현재 중력에 대한 작업을 시작했습니다 (C #에서 XNA 사용).

중력은 여전히 ​​조정이 필요하지만 그렇게하려면 물리 계산에서 일부 성능 문제를 해결해야합니다.

이것은 100 개의 객체를 사용하고 있으며, 일반적으로 물리 계산없이 1000 개의 객체를 렌더링하는 것은 300 FPS (내 FPS 캡)를 훨씬 초과하지만 10 개 이상의 객체는 게임 (및 실행되는 단일 스레드)을 물리 계산을 할 때 무릎.

나는 스레드 사용을 확인했고 첫 번째 스레드는 모든 작업에서 스스로를 죽이고 있었으므로 다른 스레드에서 물리 계산을 수행해야한다고 생각했습니다. 그러나 다른 스레드에서 Gravity.cs 클래스의 Update 메서드를 실행하려고 할 때 Gravity의 Update 메서드에 아무 것도 없어도 게임은 여전히 ​​2 FPS로 떨어집니다.

Gravity.cs

public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in entityEngine.Entities)
        {
            Vector2 Force = new Vector2();

            foreach (KeyValuePair<string, Entity> e2 in entityEngine.Entities)
            {
                if (e2.Key != e.Key)
                {
                    float distance = Vector2.Distance(entityEngine.Entities[e.Key].Position, entityEngine.Entities[e2.Key].Position);
                    if (distance > (entityEngine.Entities[e.Key].Texture.Width / 2 + entityEngine.Entities[e2.Key].Texture.Width / 2))
                    {
                        double angle = Math.Atan2(entityEngine.Entities[e2.Key].Position.Y - entityEngine.Entities[e.Key].Position.Y, entityEngine.Entities[e2.Key].Position.X - entityEngine.Entities[e.Key].Position.X);

                        float mult = 0.1f *
                            (entityEngine.Entities[e.Key].Mass * entityEngine.Entities[e2.Key].Mass) / distance * distance;

                        Vector2 VecForce = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
                        VecForce.Normalize();

                        Force = Vector2.Add(Force, VecForce * mult);
                    }
                }
            }

            entityEngine.Entities[e.Key].Position += Force;
        }

    }

그래, 알아 중첩 된 foreach 루프이지만 중력 계산을 수행하는 다른 방법을 모르겠습니다. 이것이 효과가있는 것 같습니다. 너무 집중적이므로 자체 스레드가 필요합니다. (누군가이 계산을 수행하는 매우 효율적인 방법을 알고 있더라도 대신 여러 스레드에서 수행하는 방법을 알고 싶습니다.)

EntityEngine.cs (Gravity.cs 인스턴스 관리)

public class EntityEngine
{
    public Dictionary<string, Entity> Entities = new Dictionary<string, Entity>();
    public Gravity gravity;
    private Thread T;


    public EntityEngine()
    {
        gravity = new Gravity(this);
    }


    public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in Entities)
        {
            Entities[e.Key].Update();
        }

        T = new Thread(new ThreadStart(gravity.Update));
        T.IsBackground = true;
        T.Start();
    }

}

EntityEngine은 Game1.cs에서 만들어지며 Update () 메서드는 Game1.cs에서 호출됩니다.

게임이 업데이트 될 때마다 별도의 스레드에서 실행되도록 Gravity.cs에서 물리 계산이 필요하므로 계산으로 인해 게임이 엄청나게 낮은 (0-2) FPS로 느려지지 않습니다.

이 스레딩 작업을 어떻게 수행합니까? (누구나 가지고 있다면 개선 된 행성 중력 시스템에 대한 제안은 환영합니다)

또한 스레딩을 사용하지 않아야하거나 잘못 사용할 위험에 대한 교훈을 찾고 있지 않습니다. 어떻게 해야하는지에 대한 정답을 찾고 있습니다. 나는 이미 이해하거나 도움이되는 작은 결과 로이 질문에 인터넷 검색을 한 시간을 보냈습니다. 나는 무례한 것을 의미하지는 않지만, 항상 의미있는 정답을 얻기위한 프로그래밍 멍청한 놈처럼 어려운 것 같습니다. 나는 보통 너무 복잡한 답변을 얻습니다. 누군가 내가하고 싶은 일을해서는 안되는 이유를 말하고 대안을 제공하지 않습니다 (유용한).

도와 주셔서 감사합니다!

편집 : 내가 얻은 답변을 읽은 후에 실제로 여러분이 관심을 가지고 있으며 효과가있는 답변을 내놓으려는 것이 아니라는 것을 알았습니다. 나는 하나의 돌로 두 마리의 새를 죽이고 싶었지만 (성능 향상 및 multlthreading의 기본 사항 배우기) 대부분의 문제는 내 계산에 있으며 스레딩은 성능 향상에 가치가있는 것보다 훨씬 번거 롭습니다. 모두 감사합니다. 답을 다시 읽고 학교에 다녀 오면 답을 다시 시도하겠습니다. 다시 감사합니다!


[위의 스레딩 시스템 업데이트]가 지금 무엇을합니까 (작동합니까)? Btw 게임주기에서 최대한 빨리 시작했습니다. 예를 들어 엔터티가 업데이트되기 전에.
ThorinII

2
중첩 루프 내부의 Trig 호출은 아마도 가장 큰 히트 일 것입니다. 그것들을 제거 할 수있는 방법을 찾으면 kO(n^2)문제를 크게 줄일 수 있습니다.
RBarryYoung

1
실제로 삼각 호출은 완전히 불필요합니다 . 먼저 벡터에서 각도를 계산 한 다음이를 사용하여 지정된 방향을 가리키는 다른 벡터를 생성합니다. 그런 다음 해당 벡터를 정규화하지만 sin² + cos² ≡ 1이미 정규화되었으므로! 관심있는 두 개체를 연결하는 원래 벡터를 사용하여이 벡터를 정규화했을 수 있습니다. 어떤 삼각대도 필요하지 않습니다.
leftaroundabout

XNA는 더 이상 사용되지 않습니까?
jcora

@yannbane 그 질문은 토론에 도움이되는 것을 추가하지 않습니다. 그리고 XNA의 상태는 더 이상 사용되지 않는 정의에 맞지 않습니다.
세스 배틴

답변:


36

여기있는 것은 전형적인 O (n²) 알고리즘입니다. 문제의 근본 원인은 스레딩과 관련이 없으며 알고리즘의 복잡도가 높다는 사실과 관련이 있습니다.

이전에 "Big O"표기법을 사용하지 않은 경우 기본적으로 n 개의 요소 에 대해 작업하는 데 필요한 작업 수를 의미합니다 (이는 단순화 된 설명입니다). 100 개의 요소가 루프의 내부 부분을 10000 회 실행 중 입니다.

게임 개발 에서 데이터의 양이 적고 (고정 또는 제한이있는 경우) 알고리즘이 빠르지 않은 한 일반적으로 O (n²) 알고리즘 을 사용하지 않으려 고합니다.

모든 엔티티가 다른 모든 엔티티에 영향을 미쳤다면 필요에 따라 O (n²) 알고리즘이 필요합니다. 그러나 실제로는 소수의 엔터티 만 상호 작용하는 것처럼 보이 if (distance < ...)므로 " 공간 분할 " 이라는 것을 사용하여 작업 수를 크게 줄일 수 있습니다 .

이것은 상당히 자세한 주제이며 다소 게임마다 다르므로 자세한 내용은 새로운 질문을하는 것이 좋습니다. 다음으로 넘어 갑시다 ...


코드의 주요 성능 문제 중 하나는 매우 간단합니다. 이것은 엄청나게 느립니다 :

foreach (KeyValuePair<string, Entity> e in Entities)
{
    Entities[e.Key].Update();
}

이미 가지고있는 객체에 대해 문자열, 모든 반복 (다른 루프에서 여러 번)으로 사전 조회를 수행하고 있습니다!

당신은 이것을 할 수 있습니다 :

foreach (KeyValuePair<string, Entity> e in Entities)
{
    e.Value.Update();
}

또는 당신은 이것을 할 수 있습니다 : (개인적으로 이것을 더 좋아합니다. 둘 다 같은 속도 여야합니다)

foreach (Entity e in Entities.Values)
{
    e.Update();
}

문자열을 이용한 사전 조회는 매우 느립니다. 직접 반복하는 것이 훨씬 빠릅니다.

하지만 실제로 이름별로 항목을 조회해야하는 빈도는 ? 얼마나 자주 반복해야하는지에 비해? 당신이 드물게 이름 조회를 할 수없는 경우에 엔티티를 저장하는 고려 List(그들에게 줄 Name멤버).

실제로 가지고있는 코드는 비교적 사소합니다. 프로파일 링하지는 않았지만 대부분의 실행 시간이 반복되는 사전 검색으로 이동합니다 . 이 문제를 해결하면 코드가 "충분히 빠를"수 있습니다.

편집 : 다음 가장 큰 문제는 아마도 호출 Atan2즉시와 벡터로 다시 변환 한 후와 SinCos! 벡터를 직접 사용하십시오.


마지막으로 스레딩과 코드의 주요 문제를 해결해 보겠습니다.

가장 분명한 것은 : 매 프레임마다 새로운 스레드를 만들지 마십시오! 스레드 객체는 "무거운"것입니다. 이것에 대한 가장 간단한 해결책은 단순히 ThreadPool대신 사용하는 것입니다.

물론 그렇게 간단하지 않습니다. 두 번째 문제로 넘어 갑시다 . 두 스레드의 데이터를 한 번에 만지지 마십시오! 적절한 스레드 안전 인프라를 추가하지 않아도됩니다.

당신은 기본적으로 가장 끔찍한 방식 으로 기억을 훔칩니다 . 여기에는 스레드 안전성이 없습니다. gravity.Update시작중인 여러 " "스레드 중 하나가 예기치 않은 시간에 다른 스레드에서 사용중인 데이터를 덮어 쓸 수 있습니다. 한편, 주요 스레드는 이러한 모든 데이터 구조에도 영향을 미치게됩니다. 이 코드로 인해 재현하기 어려운 메모리 액세스 위반이 발생하더라도 놀라지 않을 것입니다.

이 스레드와 같은 것을 안전하게 만드는 것은 어렵고 상당한 성능 오버 헤드를 추가 하여 종종 노력할 가치가 없습니다.


그러나 어쨌든 그렇게하는 방법에 대해 훌륭하게 묻는 것처럼, 그것에 대해 이야기합시다 ...

일반적으로 나는 스레드가 기본적으로 "화재하고 잊어 버리는"간단한 것을 연습하여 시작하는 것이 좋습니다. 오디오 재생, 디스크에 무언가 쓰기 등. 결과를 주 스레드로 다시 공급해야 할 때 상황이 복잡해집니다.

기본적으로 문제에 대한 세 가지 접근 방식이 있습니다.

1) 스레드 전체에서 사용하는 모든 데이터를 잠그십시오 . C #에서 이것은 lock문장 으로 상당히 간단 해집니다 .

일반적으로 new object일부 데이터 세트를 보호하기 위해 잠금을 위해 특별히 잠금을 생성하고 유지 합니다 (일반적으로 공용 API를 작성할 때만 발생하지만 안전하지만 스타일은 동일합니다). 그런 다음 자신 이 보호하는 데이터에 액세스 할 때 마다 잠금 개체를 잠 가야 합니다!

물론 어떤 스레드가 사용 중이기 때문에 어떤 스레드가 "잠금"상태이고 다른 스레드가 액세스하려고하면 두 번째 스레드는 첫 번째 스레드가 완료 될 때까지 대기해야합니다. 따라서 병렬로 수행 할 수있는 작업을 신중하게 선택하지 않으면 기본적으로 단일 스레드 성능 (또는 악화)이 발생합니다.

따라서 귀하의 경우 엔터티 컬렉션에 닿지 않는 다른 코드가 병렬로 실행되도록 게임을 설계 할 수 없다면이 작업을 수행 할 필요가 없습니다.

2) 데이터를 스레드에 복사하고 처리 한 다음 완료되면 다시 결과를 꺼내십시오.

정확하게 구현하는 방법은 수행중인 작업에 따라 다릅니다. 그러나 분명히 이것은 잠재적으로 고가의 복사 작업 (또는 2 개)을 포함하여 많은 경우 단일 스레드 작업을 수행하는 것보다 느릴 수 있습니다.

물론 백그라운드에서 수행해야 할 다른 작업이 있어야합니다. 그렇지 않으면 주 스레드가 다른 스레드가 완료 될 때까지 기다렸다가 데이터를 다시 복사 할 수 있습니다!

3) 스레드 안전 데이터 구조를 사용하십시오.

이것들은 단일 스레드보다 약간 느리고 단순한 잠금보다 사용하기가 종종 어렵습니다. 주의 깊게 사용하지 않으면 잠금 문제 (단일 스레드로 성능 저하)가 여전히 나타날 수 있습니다.


마지막으로, 이것은 프레임 기반 시뮬레이션이기 때문에 메인 스레드가 다른 스레드가 결과를 제공 할 때까지 기다려야 프레임을 렌더링하고 시뮬레이션을 계속할 수 있습니다. 전체 설명은 여기에 넣기가 너무 길지만 기본적으로 Monitor.Waitand 사용법을 배우고 싶을 것 Monitor.Pulse입니다. 여기 당신을 시작하는 기사가 있습니다 .


이러한 접근법에 대한 구체적인 구현 세부 사항 (마지막 비트 제외) 또는 코드를 제공하지 않았다는 것을 알고 있습니다. 우선, 다루어야 할 것이 많습니다 . 둘째, 코드 자체에는 적용 할 수있는 것이 없습니다. 스레딩을 추가하여 전체 아키텍처에 접근해야합니다.

스레딩은 당신이 가지고있는 코드를 마술처럼 빨리하지 않습니다-그것은 단지 당신이 동시에 다른 것을 할 수있게 해줍니다!


8
내가 할 수 있다면 +10 여기에 핵심 문제가 요약되어 있으므로 마지막 문장을 소개로 맨 위로 이동할 수 있습니다. 다른 스레드에서 코드를 실행해도 동시에 다른 작업이 없으면 마술처럼 렌더링 속도가 빨라지지 않습니다. 그리고 렌더러는 아마도 쓰레드가 끝나기를 기다릴 것입니다. 그러나 그것이 실패하고 어떻게 알 수 있습니까?
LearnCocos2D

나는 스레딩이 내가 필요한 것이 아니라고 확신합니다. 길고 지식이 풍부한 정보에 감사드립니다! 성능 향상에 관해서는 귀하와 다른 사람들이 제안한 변경 사항을 적용했지만 60 개가 넘는 객체를 처리 할 때 여전히 성능이 저하됩니다. N-Body 시뮬레이션 효율에 더 초점을 맞춘 다른 질문을하는 것이 최선이라고 생각합니다. 그래도 이것에 대한 내 대답을 얻습니다. 감사!
우편 배달부

1
당신은 환영합니다, 그것이 기뻤습니다 :) 당신이 신선한 질문을 게시 할 때, 나와 다른 사람이 그것을 볼 수 있도록 여기에 링크를 드롭하십시오.
앤드류 러셀

@Postman이 답변의 일반적인 내용에 동의하지만 이것이 기본적으로 스레딩을 활용하는 PERFECT 알고리즘이라는 사실을 완전히 놓친 것 같습니다. 그들이 GPU 에서이 작업을 수행하는 이유가 있으며 쓰기를 두 번째 단계로 이동하면 사소한 병렬 알고리즘이기 때문입니다. 잠금 또는 복사 또는 스레드 안전 데이터 구조가 필요하지 않습니다. 간단한 Parallel.ForEach와 문제없이 완료되었습니다.
질긴 검볼

@ChewyGumball 매우 유효한 포인트입니다! 그리고 Postman은 자신의 알고리즘을 2 단계로 만들어야하지만, 어쨌든 2 단계 여야합니다. 그러나 Parallel오버 헤드가없는 것은 아니라는 점을 지적 할 가치가 있습니다. 특히 작은 데이터 세트와 상대적으로 빠른 코드 조각에 대해 프로파일 링해야합니다. 물론,이 경우 병렬 처리를하는 것보다는 알고리즘 복잡성을 줄이는 것이 여전히 낫습니다.
Andrew Russell

22

언뜻보기에 시도해야 할 것이 있습니다. 먼저 충돌 검사를 줄이려면 quadtree 와 같은 일종의 공간 구조를 사용하여이를 수행 할 수 있습니다 . 이렇게하면 쿼리 엔터티 만 첫 번째 엔터티를 닫을 수 있으므로 두 번째 foreach 수를 줄일 수 있습니다.

스레딩 관련 : 업데이트를 할 때마다 스레드를 만들지 마십시오. 이 오버 헤드로 인해 속도가 빨라지는 것보다 속도가 느려질 수 있습니다. 대신 하나의 충돌 스레드를 작성하여 작동하도록하십시오. 구체적인 코드 붙여 넣기-이 코드 접근법은 없지만 스레드 동기화 및 C #의 백그라운드 작업자에 대한 기사가 있습니다.

또 다른 요점은 foreach 루프에서 entityEngine.Entities[e.Key].Texture이미 foreach 헤더의 dict에 액세스했기 때문에 수행 할 필요가 없다는 것입니다 . 대신 당신은 쓸 수 있습니다 e.Texture. 나는 이것의 영향에 대해 정말로 모른다. 단지 당신에게 알리고 싶었다.)

마지막 한 가지 : 현재 모든 엔터티를 다시 확인하는 중입니다. 첫 번째 및 두 번째 foreach 루프에서 쿼리되기 때문입니다.

엔티티 A와 B가 2 개인 예 :

pick A in first foreach loop
   pick A in second foreach loop
      skip A because keys are the same
   pick B in second foreach loop
      collision stuff
pick B in first foreach loop
   pick A in second foreach loop
      collision stuff
   pick B in second foreach loop
      skip B because keys are the same

이것은 가능한 접근 방법이지만 충돌 검사의 절반을 건너 뛰고 A와 B를 한 번에 처리 할 수 ​​있습니다.

희망이 시작됩니다 =)

추신 : 당신이 듣고 싶지 않다고 말하더라도 : 충돌 감지를 동일한 스레드에서 유지하고 속도를 높이십시오. 스레딩은 좋은 생각처럼 보이지만 지옥과 동기화해야합니다. 충돌 점검이 업데이트 (스레딩 사유)보다 느린 경우, 선박이 이미 이동 한 후 충돌이 트리거되고 그 반대도 발생하므로 충돌과 오류가 발생합니다. 나는 당신을 실망시키고 싶지 않습니다. 이것은 단지 개인적인 경험입니다.

EDIT1 : QuadTree 튜토리얼과의 링크 (Java) : http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/


10
중력 시뮬레이션에 쿼드 / 옥트리를 사용하는 것에 대한 좋은 점은 먼 입자를 무시하는 대신 나무의 각 가지에 모든 입자의 총 질량과 질량 중심을 저장하고이를 사용하여 평균 중력 효과를 계산할 수 있다는 것입니다 다른 먼 입자에있는이 가지에있는 모든 입자 중 이것은로 알려져있다 반스 - 헛 알고리즘 , 그리고 그것은 프로가 무엇을 사용 .
Ilmari Karonen

10

솔직히 가장 먼저 해야 할 일은 더 나은 알고리즘으로 전환하는 것입니다.

가장 좋은 경우에도 시뮬레이션을 병렬화하면 CPU 수 × CPU 당 코어 수 × 시스템에서 사용할 수있는 코어 당 스레드 수 (즉, 최신 PC의 경우 4-16 사이)만큼만 속도를 높일 수 있습니다. (코드를 GPU로 이동하면 추가 개발 복잡성과 스레드 당 기준선 계산 속도가 낮아짐에 따라 훨씬 더 인상적인 병렬화 요소를 얻을 수 있습니다 .) 예제 코드와 같은 O (n²) 알고리즘을 사용하면 예제 코드와 같이 현재 가지고있는 입자의 2 ~ 4 배 많은 입자를 사용하십시오.

반대로,보다 효율적인 알고리즘으로 전환하면 100 ~ 10000 (순수하게 추측 된 수)의 계수로 시뮬레이션 속도를 쉽게 높일 수 있습니다. 공간적 세분화를 사용하는 우수한 n-body 시뮬레이션 알고리즘의 시간 복잡도 는 "거의 선형"인 O (n log n)과 거의 비슷하므로 처리 할 수있는 입자 수의 거의 동일한 증가 요인을 기대할 수 있습니다. 또한, 그것은 여전히 하나 개의 스레드를 사용하는 것, 그래서 여전히 병렬화의 여지가 될 그 꼭대기에 .

어쨌든, 다른 답변에서 지적했듯이, 많은 수의 상호 작용 입자를 효율적으로 시뮬레이션하는 일반적인 방법은 쿼드 트리 (2D) 또는 옥트리 (3D)로 구성하는 것입니다. 특히 중력을 시뮬레이션하기 위해 사용하려는 기본 알고리즘은 Barnes-Hut 시뮬레이션 알고리즘입니다.이 알고리즘 에서는 쿼드 / 옥트리의 각 셀에 포함 된 모든 입자의 총 질량 (및 질량 중심)을 저장합니다. 이를 사용하여 다른 먼 입자에 대한 세포의 입자의 평균 중력 효과를 근사화하십시오.

당신에 의해 반스 - 헛 알고리즘에 대한 설명과 튜토리얼을 많이 찾을 수 있습니다 인터넷 검색 그것을하지만, 여기에 좋은 당신이 시작하는 간단한 일이다 하면서, 여기에 고급 구현의 설명이다 은하 충돌의 GPU 시뮬레이션에 사용되는가.


6

스레드와 관련이없는 또 다른 최적화 답변. 미안합니다.

모든 쌍의 Distance ()를 계산합니다. 이것은 제곱근을 취하는 것과 관련이 있으며, 느립니다. 또한 실제 크기를 얻기 위해 여러 개체 조회가 필요합니다.

대신 DistanceSquared () 함수를 사용하여이를 최적화 할 수 있습니다. 두 객체가 상호 작용할 수있는 최대 거리를 미리 계산하고 제곱 한 다음 DistanceSquared ()와 비교합니다. 제곱 거리가 최대 거리 내에있는 경우에만 제곱근을 취하여 실제 물체 크기와 비교하십시오.

편집 :이 최적화는 주로 충돌을 테스트 할 때 주로 사용됩니다. 현재 내가 실제로하고있는 것이 아니라는 것을 알았습니다 (확실히 어느 시점에있을 것입니다). 모든 입자의 크기 / 질량이 비슷한 경우에도 상황에 여전히 적용 할 수 있습니다.


네. 이 솔루션은 괜찮을 수 있지만 (정확한 손실은 무시할 수 있지만) 물체의 질량이 많이 다를 때 문제가 발생합니다. 일부 물체의 질량이 매우 큰 반면 일부 물체의 질량이 매우 작 으면 합리적인 최대 거리가 더 큽니다. 예를 들어 작은 먼지 입자에 대한 지구 중력의 영향은 지구에서는 무시할 수 있지만 먼지 입자에는 영향을 미치지 않습니다 (먼 거리). 그러나 실제로 같은 거리에있는 두 개의 먼지 입자는 서로 크게 영향을 미치지 않습니다.
SDwarfs

사실 그것은 아주 좋은 지적입니다. 나는 이것을 충돌 테스트로 잘못 읽었지만 실제로는 반대입니다. 입자가 접촉하지 않으면 서로 영향을 미칩니다.
Alistair Buxton

3

스레딩에 대해 많이 알지 못하지만 루프가 시간이 걸리는 것 같습니다.

i = 0; i < count; i++
  j = 0; j < count; j++

  object_i += force(object_j);

이에

i = 0; i < count-1; i++
  j = i+1; j < count; j++

  object_i += force(object_j);
  object_j += force(object_i);

도울 수 있었다


1
왜 도움이 되겠습니까?

1
처음 두 개의 루프는 10 000 회 반복되지만 두 번째 루프는 4,950 회만 반복 되기 때문 입니다.
Buksy

1

이미 10 개의 시뮬레이션 된 오브젝트에 대해 큰 문제가있는 경우 코드를 최적화해야합니다! 중첩 루프는 10 * 10 반복 만 발생하고 그중 10 반복은 건너 뛰고 (동일한 객체) 내부 루프가 90 반복됩니다. 2 FPS 만 달성하면 성능이 너무 나빠서 초당 내부 루프를 180 회만 반복 할 수 있습니다.

다음을 수행 할 것을 제안합니다.

  1. 준비 / 벤치마킹 :이 루틴이 문제임을 확실히 알기 위해 작은 벤치 마크 루틴을 작성하십시오. Update()예를 들어 중력 의 방법을 여러 번 (예 : 1000 회) 실행하고 시간을 측정해야합니다. 100 개의 객체로 30FPS를 달성하려면 100 개의 객체를 시뮬레이션하고 30 회의 실행 시간을 측정해야합니다. 1 초 미만이어야합니다. 합리적인 최적화를 위해서는 이러한 벤치 마크를 사용해야합니다. 그렇지 않으면 반대를 달성하고 코드가 더 빨라야하기 때문에 코드 실행 속도를 느리게 할 것입니다.

  2. 최적화 : O (N²) 노력 문제에 대해 많은 것을 할 수는 없지만 (즉, 시뮬레이션 된 객체의 수 N으로 계산 시간이 2 차적으로 증가 함) 코드 자체를 향상시킬 수 있습니다.

    a) 코드 내에서 많은 "연관 배열"(사전) 조회를 사용합니다. 이것들은 느리다! 예를 들면 entityEngine.Entities[e.Key].Position. 그냥 사용할 수 e.Value.Position없습니까? 이것은 하나의 조회를 저장합니다. 내부 루프 전체에서이 작업을 수행하여 e 및 e2가 참조하는 객체의 속성에 액세스 할 수 있습니다. 이것을 변경하십시오! b) 루프 안에 새 Vector를 만듭니다 new Vector2( .... ). 모든 "새"호출은 일부 메모리 할당 (및 나중에 할당 해제)을 의미합니다. 이것들은 딕셔너리 조회보다 훨씬 느립니다. 이 Vector가 일시적으로 만 필요한 경우 루프 외부에 할당하고 새 객체를 만드는 대신 값을 새 값으로 다시 초기화하여 재사용하십시오. C) 당신은 예를 들면 삼각 함수를 많이 (사용 atan2cos루프 내에서). 정확도가 실제로 정확하지 않아도되는 경우 대신 조회 테이블을 사용하려고 할 수 있습니다. 이렇게하려면 값을 정의 범위로 조정하고 정수 값으로 반올림 한 후 미리 계산 된 결과 표에서 찾아보십시오. 도움이 필요하면 물어보십시오. d) 자주 사용 .Texture.Width / 2합니다. 이 값을 미리 계산하고 결과를 저장 .Texture.HalfWidth하거나 항상 양의 정수 값인 경우 비트 이동 연산 >> 1을 사용 하여 2만큼 나눌 수 있습니다.

한 번에 하나의 변경 사항 만 수행하고 벤치 마크를 통해 변경 사항을 측정하여 이것이 런타임에 미치는 영향을 확인하십시오! 어쩌면 다른 아이디어가 나쁜 동안 한 가지가 좋을 수도 있습니다 (위에서 제안 했더라도!) ...

여러 스레드를 사용하여 더 나은 성능을 달성하는 것보다 이러한 최적화가 훨씬 낫습니다! 스레드를 조정하는 데 많은 어려움이 있으므로 다른 값을 덮어 쓰지 않습니다. 또한 유사한 메모리 영역에 액세스 할 때 충돌이 발생합니다. 이 작업에 4 개의 CPU / 스레드를 사용하는 경우 프레임 속도로 2 ~ 3의 속도 만 기대할 수 있습니다.


0

객체 생성 라인없이 재 작업 할 수 있습니까?

Vector2 Force = 새로운 Vector2 ();

Vector2 VecForce = 새로운 Vector2 ((float) Math.Cos (angle), (float) Math.Sin (angle));

매번 두 개의 새 객체를 작성하는 대신 힘 값을 엔티티에 배치 할 수 있다면 성능을 향상시키는 데 도움이 될 수 있습니다.


4
Vector2XNA에서 값 유형 입니다. GC 오버 헤드가 없으며 시공 오버 헤드는 무시할 수 있습니다. 이것이 문제의 원인이 아닙니다.
앤드류 러셀

@Andrew Russell : 확실하지는 않지만 "new Vector2"를 사용하는 경우에도 여전히 그렇습니까? "new"없이 Vector2 (....)를 사용하는 경우 이는 아마도 다를 수 있습니다.
SDwarfs

1
@StefanK. C #에서는 그렇게 할 수 없습니다. 새로운 것이 필요합니다. C ++을 생각하고 있습니까?
MrKWatkins
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.