EjongHyuck About Archive Tags Github

유니티 C#에서 struct와 enum을 key로 사용하는 Dictionary의 최적화

Garbage

일반적인 상황에서 enum과 struct는 Value-Type이기 때문에 힙 할당을 하지 않으므로 가비지가 생길 일이 존재하지 않는다. 다만 아래 2가지 경우에선 가비지가 발생하게 되는 점은 잘 알려지지 않았다.

왜 가비지가 생기는가?

struct를 선언할 때 Equals(), GetHashCode() 메서드들을 구현하지 않았다면, Dictionary의 Contains 메서드를 호출할 경우 내부적으로 object로 박싱하여 object.Equals를 통해 비교 구문을 실행하게 된다. 이는 some_dictionary[key] 처럼 접근할 때에도 key의 비교 구문을 실행하기 때문에 내부적으로 박싱이 이뤄지게 된다.

당연히 박싱이 이뤄지면 가비지가 생길 수 밖에 없으며, 안타깝게도 struct에는 Vector3같은 유니티의 built-in struct들도 박싱이 이뤄진다. enum의 Equals() 메서드 또한 struct와 마찬가지로. object로 박싱하여 비교하기 때문에 가비지가 발생한다.

가비지가 생기지 않게 하려면?

가장 편한 방법은 System.Collections.Generic 네임스페이스 내부에 존재하는 IEqualityComparer<T> 인터페이스를 상속받는 클래스를 선언해서 비교하게 하는 방법이다. Dictionary 인스턴스를 새로 생성할 때 생성자에 인스턴스를 넣어주면 된다.

// Struct.
public struct SomeStruct
{
    public int a, b;
}

class SomeComparer : IEqualityComparer<SomeStruct>
{
    bool IEqualityComparer<SomeStruct>.Equals (SomeStruct x, SomeStruct y)
    {
        return x.a == y.a && x.b == y.b;
    }

    bool IEqualityComparer<SomeStruct>.GetHashCode (SomeStruct obj)
    {
        return obj.a ^ obj.b;
    }
}

// Usage.
Dictionary<SomeStruct, int> dic = new Dictionary<SomeStruct, int>(new SomeComparer());

struct 외에도 enum 역시 동일하게 적용할 수 있다.

// Enum.
public enum SomeEnum
{
    StateA = 1,
    StateB = 2
}

class SomeComparer : IEqualityComparer<SomeEnum>
{
    bool IEqualityComparer<SomeEnum>.Equals (SomeEnum x, SomeEnum y)
    {
        return (int)x == (int)y;
    }

    bool IEqualityComparer<SomeEnum>.GetHashCode (SomeEnum obj)
    {
        return ((int)obj).GetHashCode();
    }
}

// Usage.
Dictionary<SomeEnum, int> dic = new Dictionary<SomeEnum, int>(new SomeComparer());

Share this: