Unity Tips

Unity에서 Enum.Parse함수 GC 발생 줄이기

종잇장 2022. 5. 1. 18:31

 이전 글에서 Xml 파싱 코드를 만들다 Enum.Parse를 그대로 사용했는데 이 부분에서 GC가 많이 발생해서 이 부분만 때어내서 글을 써볼까 합니다.

 웬만한 상황에서는 Enum을 정수형으로 변환해서 저장하고 파싱 하는 게 가장 좋은 방법인데, Xml로 스크립팅을 하게 되는 경우 스크립팅을 하는 작업자분들이 정수형보다는 Enum의 이름으로 보는 게 작업하기 편해서 생각보다 많이 사용됩니다.

 


 

문제

이전 글에서 작업했던 코드를 Unity Profiler에서 확인 했을 때의 스크린샷입니다.

GC.Alloc 호출 수도 어어어어엄청 많고 실제 할당된 메모리도 66.4MB면... 어우..

 

 


 

구현

using System;
using System.Collections.Generic;

public static class EnumUtility
{
    static Dictionary<Type, Dictionary<string, object>> m_DicStringToEnum = new Dictionary<Type, Dictionary<string, object>>();

    public static void CacheStringToEnum(Type type)
    {
        if (m_DicStringToEnum.ContainsKey(type) == true)
            return;

        m_DicStringToEnum.Add(type, new Dictionary<string, object>());

        var names = System.Enum.GetNames(type);
        var values = System.Enum.GetValues(type);

        for (int i = 0; i < names.Length; i++)
        {
            var name = names[i];
            var value = values.GetValue(i);
            if (string.IsNullOrEmpty(name)) continue;
            if (value == null) continue;

            m_DicStringToEnum[type].Add(name, value);
        }
    }

    public static TEnum EnumParse<TEnum>(string str) where TEnum : Enum
    {
        var type =  typeof(TEnum);
        CacheStringToEnum(type);

        if (m_DicStringToEnum[type].ContainsKey(str) == false)
        {
            throw new FormatException();
        }

        return (TEnum)m_DicStringToEnum[type][str];
    }
}

 

 Enum.Parse에서 왜 GC가 발생하는지는 정확히는 모르겠지만, EnumUtility라는 static 클래스를 만들어서 Dictionary에 캐싱하여 string으로 찾도록 구현해두었습니다. (아마도 캐싱 없이 string을 만들고 버리고 반복해서 그런 게 아닐까 추측합니다)

 

자세한 코드는 GitHub에 올려두었습니다.

https://github.com/PieceOfPaper/CSharp_EnumUtility/blob/main/EnumUtility.cs

 

 


 

결과

  • Calls: 1,600,240 -> 1,400,262 (199,978 감소)
  • GC Alloc: 66.4 MB -> 60.7 MB (5.7 MB 감소)

 전체에 비해서는 조금 감소했지만 Enum 이외에도 다른 곳에서 GC를 발생시키는 문제들이 많은 코드라 이 정도 결과면 충분하다고 생각됩니다.

 Enum.Parse함수를 10만 번 호출하도록 구현되어 있었으니, Enum.Parse 한번 호출 때마다 GC.Alloc 호출 수가 2번씩 생긴다고 보면 되겠네요.

 

 

 

사실 Enum.Parse로 실제 프로젝트에서 고생해서 글로 남기게 되었습니다.

에디터에서는 별 문제없는데 안드로이드 빌드해서 테스트하니 엄청난 프레임드랍을 맛봤습니다..