Unity

[Unity] 최적화 - Yield return new

window= 2023. 11. 2. 11:38

1. Coroutine을 자주 사용하면 GC(Garbage Collection) 발생한다.

유니티에서 코루틴을 자주 사용하면 메모리 할당 및 해제가 발생하기 때문에 가비지 컬렉션의 발생이 성능 문제를 초래할 수 있다. 특히 코루틴 내에서 'yield return new'를 사용하여 객체를 생성하고 파괴하는 경우에는 가비지 컬렉션의 발생이 빈번하게 일어난다. 

 

※GC(Garbage Collection)란?

가비지 컬렉션은 메모리에서 사용하지 않는 객체를 정리해 메모리를 관리하는 프로세스이다. 게임에서 가비지 컬렉션이 자주 발생하면 불안정한 프레임류로 인해 짧은 중단이 발생하여 게임 성능에 영향을 미칠 수 있다. 따라서  게임 개발 시 가비지 컬렉션을 최대한 줄여야 한다. 가비지 컬렉션 관리를 위해서는 오브젝트 풀링, 프레임간 딜레이 추가, 프로파일링을 통해 수행할 수 있다. 

 


 

2. Coroutine 최적화 방법

YieldInstruction를 미리 캐싱해놓는 구조로 class를 하나 만들어 재사용하는 방식으로 GC를 줄인다. 

 

2.1

WaitForEndOfFrame와 WaitForFixedUpdate는 time value에 따라 인스턴스가 달라지지 않기 때문에 new 로 생성해놓고 사용한다. 

public static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();
public static readonly WaitForFixedUpdate WaitForFixedUpdate = new WaitForFixedUpdate();

 

2.2 WaitForSeconds와 WaitForSecondsRealtime 처럼 float value로 인스턴스가 달라지는 경우에는 IEqualityComparer 인터페이스와 Dictionary를 사용하여 캐싱한다. 

  private class FloatComparer : IEqualityComparer<float>
  {
      public bool Equals(float x, float y)
      {
         return x == y;
      }

      public int GetHashCode(float obj)
      {
         return obj.GetHashCode();
      }
  }
  
  private static readonly Dictionary<float, WaitForSeconds> _dicSeconds = new Dictionary<float, WaitForSeconds>(new FloatComparer());
  private static readonly Dictionary<float, WaitForSecondsRealtime> _dicSecondsRealtime = new Dictionary<float, WaitForSecondsRealtime>(new FloatComparer());

 

IEqualityComparer 란?

두 객체를 비교하여 같은지 여부를 결정하기 위해 사용되는 인터페이스로 컬랙션 클래스인 Dictionary나 HashSet에서 요소 비교를 제어한다. 

 

딕셔너리에 담긴 인스터스 호출한다.

    public static WaitForSeconds WaitForSeconds(float fSeconds)
    {
        if (!_dicSeconds.TryGetValue(fSeconds, out WaitForSeconds waitForSeconds))
            _dicSeconds.Add(fSeconds, waitForSeconds = new WaitForSeconds(fSeconds));
        return waitForSeconds;
    }

    public static WaitForSecondsRealtime WaitForSecondsRealtime(float fSeconds)
    {
        if (!_dicSecondsRealtime.TryGetValue(fSeconds, out WaitForSecondsRealtime waitForSecondsRealtime))
            _dicSecondsRealtime.Add(fSeconds, waitForSecondsRealtime = new WaitForSecondsRealtime(fSeconds));
        return waitForSecondsRealtime;
    }

 

 


3. 전체 코드 

public class YieldInstructionCache
{
    private class FloatComparer : IEqualityComparer<float>
    {
        public bool Equals(float x, float y)
        {
            return x == y;
        }

        public int GetHashCode(float obj)
        {
            return obj.GetHashCode();
        }
    }

    private static readonly Dictionary<float, WaitForSeconds> _dicSeconds = new Dictionary<float, WaitForSeconds>(new FloatComparer());
    private static readonly Dictionary<float, WaitForSecondsRealtime> _dicSecondsRealtime = new Dictionary<float, WaitForSecondsRealtime>(new FloatComparer());

    public static WaitForSeconds WaitForSeconds(float fSeconds)
    {
        if (!_dicSeconds.TryGetValue(fSeconds, out WaitForSeconds waitForSeconds))
            _dicSeconds.Add(fSeconds, waitForSeconds = new WaitForSeconds(fSeconds));
        return waitForSeconds;
    }

    public static WaitForSecondsRealtime WaitForSecondsRealtime(float fSeconds)
    {
        if (!_dicSecondsRealtime.TryGetValue(fSeconds, out WaitForSecondsRealtime waitForSecondsRealtime))
            _dicSecondsRealtime.Add(fSeconds, waitForSecondsRealtime = new WaitForSecondsRealtime(fSeconds));
        return waitForSecondsRealtime;
    }

    public static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();
    public static readonly WaitForFixedUpdate WaitForFixedUpdate = new WaitForFixedUpdate();
}

 


4. 사용 예시

yield return YieldInstructionCache.WaitForSeconds(0.1f);
yield return YieldInstructionCache.WaitForSecondsRealtime(0.2f);
yield return YieldInstructionCache.WaitForEndOfFrame;
yield return YieldInstructionCache.WaitForFixedUpdate;
반응형