EveryDay.DevUp

[Unity] Foreach의 GC의 원인 및 수정 본문

Unity/최적화

[Unity] Foreach의 GC의 원인 및 수정

EveryDay.DevUp 2021. 12. 29. 21:47

Unity 5.5 미만 버전에서 foreach를 사용할 경우 가비지가 발생하는 이슈가 있었다.

원인

- Mono C# Unity 버전에서 foreach loop 종료 시 값을 강제로 박싱

- 값 형식의 열거자를 생성하였는데, 해당 열거자를 사용할 경우 loop가 종료되는 시점에 IDisposable 인터페이스를 구현 해야 했음

- 인터페이스는 참조 형식이기 때문에, 값 형식을 인터페이스로 변환하는 중에 박싱이 발생하게 됨

참고 : https://everyday-devup.tistory.com/106

 

[Unity] Struct (구조체)

Struct (구조체)? - 데이터와 관련 기능을 캡슐화할 수 있는 값 형식 캡슐화 : 데이터와 기능을 하나로 묶고, 구현 내용 일부를 외부에 감춤 특징 1. 매개 변수가 없는 생성자를 선언할 수 없음 ( 단,

everyday-devup.tistory.com

대응

- Unity의 C# 컴파일러가 최신 버전의 Mono 컴파일러로 업그레이드 되면서 수정 됨

List<int> intList = new List<int>{ 1, 2, 3 };
foreach( int i in intList) {}

// foreach IL GetEnumerator
[__DynamicallyInvokable]
public List<T>.Enumerator GetEnumerator() => new List<T>.Enumerator(this);

// valueType의 eumerator는 IDisposable interface를 구현
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator

public interface IDisposable
{
    [__DynamicallyInvokable]
    void Dispose();
}

// Unity 버전 5.5 미만에서는
finally
{

  IL_0030: ldloc.2      // V_2
  // valueType에서 interface (참조형식) 박싱을 해서 Dispose를 호출 
  IL_0031: box          valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
  IL_0036: callvirt     instance void [mscorlib]System.IDisposable::Dispose()
  IL_003b: endfinally   

}

// Unity 버전 5.5 이상에서는
finally
{
   IL_0035: ldloca.s     V_0
   // 박싱이 발생하지 않음, ValueType List<T>.Enumerator의 포인터를 직접 넘겨주게 변경 됨.
   // C# constrained접두사는 callvirt this Type 이 값 형식 인지 또는 참조 형식 인지에 
   // 관계 없이 일관 된 방식으로 명령을 수행할 수 있도록 설계 되었습니다.
   IL_0037: constrained. valuetype [netstandard]System.Collections.Generic.List`1/Enumerator<int32>
   IL_003d: callvirt     instance void [netstandard]System.IDisposable::Dispose()
   IL_0042: endfinally
}

 

* 출처 및 참고 블로그 

https://docs.unity3d.com/kr/2019.4/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html

 

관리되는 힙에 대한 이해 - Unity 매뉴얼

많은 Unity 개발자들이 겪는 또 다른 일반적인 문제는 예기치 않은 관리되는 힙(managed heap)의 확장입니다. Unity에서 관리되는 힙은 축소되는 것보다 더 쉽게 확장됩니다. 이와 더불어, Unity의 가비

docs.unity3d.com

https://m.cafe.naver.com/ca-fe/web/cafes/indiedev/articles/13188?useCafeId=false&or=m.search.naver.com&query=foreach%2B%EA%B5%AC%EC%A1%B0%2B%EB%AC%B8%EC%A0%9C&buid=09ddb1ed-e3f0-43b1-a235-5f194b615103&art=ZXh0ZXJuYWwtc2VydmljZS1uYXZlci1ldGMtZm9yLWNvbW1lbnQ.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYWZlVHlwZSI6IkNBRkVfSUQiLCJhcnRpY2xlSWQiOjEzMTg4LCJpc3N1ZWRBdCI6MTYxNDI5NzMzMTczMiwiY2FmZUlkIjoyODE4MzkzMX0.kB0HsxGiUsEG6bu-qN_PZm9Z8PFnvtfHLiIe7vk11Vg 

 

(중급) 유니티의 Foreach는 여전히 악의 근원일까요?

대한민국 모임의 시작, 네이버 카페

cafe.naver.com

https://docs.microsoft.com/ko-kr/dotnet/api/system.reflection.emit.opcodes.constrained?view=net-6.0 

 

OpCodes.Constrained 필드 (System.Reflection.Emit)

가상 메서드가 호출되는 형식을 제한합니다.Constrains the type on which a virtual method call is made.

docs.microsoft.com