Unity로 게임을 제작할 때 GC를 어떻게 설명해야합니까?


15

* 내가 아는 한 iOS 용 Unity3D는 모노 런타임을 기반으로하며 모노는 세대 별 표시 및 스위프 GC 만 있습니다.

이 GC 시스템은 게임 시스템을 중지시키는 GC 시간을 피할 수 없습니다. 인스턴스 풀링은 CLR의 기본 클래스 라이브러리에서 인스턴스화를 제어 할 수 없기 때문에이를 줄일 수 있지만 완전하지는 않습니다. 작고 빈번한 숨겨진 인스턴스는 결국 결정적이지 않은 GC 시간을 증가시킵니다. 완전한 GC를 주기적으로 강제하면 성능이 크게 저하됩니다 (실제로 Mono 강제 GC가 가능합니까?).

그렇다면 큰 성능 저하없이 Unity3D를 사용할 때이 GC 시간을 어떻게 피할 수 있습니까?


3
최신 Unity Pro 프로파일 러에는 특정 함수 호출에서 생성 된 가비지 양이 포함되어 있습니다. 이 도구를 사용하면 가비지를 생성 할 수있는 다른 장소를 즉시 확인할 수 있습니다.
Tetrad

답변:


18

운 좋게도 COMPACT Mono 빌드는 세대 별 GC를 사용합니다 (단순한 목록을 유지하는 WinMo / WinPhone / XBox와 같은 Microsoft와는 대조적 임).

게임이 단순하다면 GC는 잘 처리해야하지만 여기에 몇 가지 포인터가 있습니다.

조기 최적화

먼저 문제를 해결하기 전에 이것이 실제로 문제인지 확인하십시오.

풀링 비싼 참조 유형

자주 작성하거나 구조가 깊은 참조 유형을 풀링해야합니다. 각각의 예는 다음과 같습니다.

  • 자주 생성 : 총알 게임Bullet객체 .
  • 심층 구조 : AI 구현을위한 의사 결정 트리.

을 사용하는 Stack대부분의 구현과 달리을 풀로 사용해야합니다 Queue. 그 이유 Stack는 객체를 풀에 반환하면 다른 객체가 즉시 객체를 가져 오기 때문입니다. 활성 페이지 또는 운이 좋으면 CPU 캐시에있을 가능성이 훨씬 높습니다. 아주 조금 더 빠릅니다. 또한 항상 수영장의 크기를 제한하십시오 (한도를 초과 한 경우 '체크인'은 무시하십시오).

삭제하기 위해 새 목록을 작성하지 마십시오

List실제로 새로운 것을 만들지 마십시오 Clear(). 백엔드 어레이를 재사용하고 어레이 할당 및 사본로드를 저장할 수 있습니다. 이 시도와 마찬가지로 초기 용량이 의미있는 목록을 작성하십시오 (이것은 제한이 아니며 시작 용량 임). 정확할 필요는 없으며 추정 일뿐입니다. 이것은 기본적으로 a를 제외한 모든 컬렉션 유형에 적용됩니다 LinkedList.

가능한 경우 구조 배열 (또는 목록)을 사용하십시오.

객체간에 구조체를 전달하면 사용 구조체 (또는 일반적으로 값 형식)의 이점이 거의 없습니다. 예를 들어, 대부분의 '좋은'입자 시스템에서 개별 입자는 방대한 배열에 저장됩니다. 배열과 색인은 입자 자체 대신 전달됩니다. 이것이 잘 작동하는 이유는 GC가 배열을 수집해야 할 때 내용을 완전히 건너 뛸 수 있기 때문입니다 (기본 배열이므로 여기서 할 일이 없습니다). 따라서 10,000 개의 객체를 보는 대신 GC는 단순히 1 개의 배열을보아야합니다. 다시 말하지만, 이는 value 유형에서만 작동합니다 .

RoyT 후. 실행 가능하고 건설적인 피드백을 제공하여 더 확장해야한다고 생각합니다. 방대한 양의 엔티티 (수만에서 수만)를 다룰 때만이 기술을 사용해야합니다. 또한이 있어야 구조체를 수있는 하지해야 모든 참조 형식 필드를 가지고 명시 적으로 형식의 배열에 거주해야합니다. 그의 의견과 달리 우리는 클래스의 필드 일 가능성이 높은 배열에 배열을 배치합니다. 즉 힙을 쌓을 것입니다 (힙 할당을 피하려는 것이 아니라 단순히 GC 작업을 피하려고 함). 우리는 실제로 GC가 단순히 O(1)작업 대신 작업 에서 볼 수있는 많은 값을 가진 연속적인 메모리 덩어리라는 사실에 관심을 기울 O(n)입니다.

또한 이러한 배열을 가능한 한 응용 프로그램 시작에 가깝게 할당하여 조각화가 발생할 가능성을 완화 하거나 GC가 이러한 청크를 이동하려고 할 때 과도한 작업을 수행해야합니다 ( 내장 유형 대신 하이브리드 연결 목록 사용을 고려 하십시오) List).

GC.Collect ()

이것은 세대 별 GC 를 사용하여 발쏠 수있는 가장 좋은 방법입니다 ( "성능 고려 사항"참조). EXTREME 가비지를 생성 한 경우에만 호출해야 하며, 문제가 될 수있는 경우는 레벨의 컨텐츠를로드 한 직후입니다. 심지어 1 세대 만 수집해야합니다 (GC.Collect(0); ) 3 세대로 객체를 홍보하는 것을 막기 위해

IDisposable 및 필드 널링

더 이상 객체가 필요하지 않은 경우 (제한된 객체의 경우) null 필드를 사용하는 것이 좋습니다. 그 이유는 GC 작동 방식에 대한 세부 사항에 있습니다. 현재 컬렉션에서 다른 객체가 제거되어 해당 객체가 루팅되지 않은 경우에도 루팅되지 않은 (즉, 참조 된) 객체 만 제거합니다 ( 주 : GC에 따라 다름) 사용하는 맛-일부는 실제로 체인을 청소합니다). 또한 개체가 컬렉션에 남아 있으면 즉시 다음 세대로 승격됩니다. 즉, 필드에 남아있는 모든 개체는 컬렉션 중에 승격됩니다. 각각의 연속적인 세대는 기하 급수적으로 비용이 많이 들고 비 정기적으로 발생합니다.

다음 예를 보자.

MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)

MyFurtherNestedObject멀티 메가 바이트 객체가 포함되어 있으면 GC가 오랫동안이를 보지 않을 수 있습니다. 실수로 G3으로 승격했기 때문입니다. 이 예제와 대조하십시오 :

MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// Dispose
MyObject (G1)
MyNestedObject (G1)
MyFurtherNestedObject (G1)
// G1 Collection

Disposer 패턴을 사용하면 개체가 개인 필드를 지우도록 요청하는 예측 가능한 방법을 설정할 수 있습니다. 예를 들면 다음과 같습니다.

public class MyClass : IDisposable
{
    private MyNestedType _nested;

    // A finalizer is only needed IF YOU CONTROL UNMANAGED RESOURCES
    // ~MyClass() { }

    public void Dispose()
    {
       _nested = null;
    }
}

1
WTF에 대해? Windows의 .NET Framework는 절대적으로 세대입니다. 그들의 클래스에는 GetGeneration 메소드도 있습니다.
DeadMG

2
Windows의 .NET은 세대 별 가비지 콜렉터를 사용하지만 WP7 및 Xbox360에서 사용되는 .NET Compact Framework는 GC가 더 제한적이지만 전체 목록이 아닌 단순한 목록 일 수도 있습니다.
Roy T.

Jonathan Dickinson : 구조체가 항상 정답은 아니라고 덧붙이고 싶습니다. .Net 및 아마도 Mono에서도 구조체는 스택에 있어야 할 필요는 없지만 (가비지 수집기가 필요한 힙)에도 살 수 있습니다. 이것에 대한 규칙은 상당히 복잡하지만 일반적으로 구조체에 값이 아닌 유형이 포함되어있을 때 발생합니다.
Roy T.

1
@RoyT. 위의 의견을 참조하십시오. 나는 구조체가 파티클 시스템과 같은 배열의 많은 양의 데이터에 적합하다는 것을 나타 냈습니다. 배열의 GC 구조체가 걱정된다면 갈 길입니다. 입자와 같은 데이터
Jonathan Dickinson

10
@DeadMG는 또한 WTF를 강력하게 요구하지 않습니다. 실제로 답변을 제대로 읽지 않았다는 것을 고려하십시오. 이와 같이 일반적으로 잘 작동하는 커뮤니티에서 증오 성 댓글을 작성하기 전에 성질을 진정시키고 답을 다시 읽으십시오.
Jonathan Dickinson

7

필요한 것을 호출하지 않으면 기본 라이브러리 내에서 자동으로 동적 인스턴스화가 거의 발생하지 않습니다. 대부분의 메모리 할당 및 할당 해제는 자체 코드에서 비롯되며이를 제어 할 수 있습니다.

내가 이해하는 것처럼, 이것을 완전히 피할 수는 없습니다. 가능하면 객체를 재활용하고 풀링하고 클래스 대신 구조체를 사용하고 UnityGUI 시스템을 피하고 일반적으로 시간 동안 동적 메모리에 새 객체를 만드는 것을 피하기 만하면됩니다 성능이 중요 할 때.

특정 시간에 강제로 가비지 콜렉션을 강제 할 수도 있습니다. 도움이 될 수도 있고 그렇지 않을 수도 있습니다. http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html


2
GC 강요의 의미를 실제로 설명해야합니다. GC 휴리스틱 및 레이아웃에 큰 영향을 미치며 잘못 사용하면 더 큰 메모리 문제가 발생할 수 있습니다. XNA / MVP / 스태프 커뮤니티는 일반적으로로드 후 컨텐츠를 수집을 강제 할 유일한 시간으로 간주합니다. 한 문장 만 문제에 바치면 완전히 언급하지 마십시오. GC.Collect()= 지금 메모리를 확보하지만 나중에 문제가 발생할 가능성이 큽니다.
Jonathan Dickinson

아니요. GC에 대해 더 많이 알고 있기 때문에 GC를 강요 할 때의 의미를 설명해야합니다. :) 그러나 Mono GC는 반드시 Microsoft GC와 같을 필요는 없으며 위험이 동일하지 않을 수도 있습니다.
Kylotan

여전히 +1입니다. 계속해서 GC에 대한 개인적인 경험에 대해 자세히 설명했습니다.
Jonathan Dickinson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.