본문 바로가기

[Unity]/Unity

[Unity] IL2CPP : IL2CPP의 제한사항 (Generic)

728x90
반응형

개발하면서 평소 궁금하던 것들을 일기장 형식으로 작성한 것입니다.
참고용으로 봐주시고 피드백이 있다면 언제든지 댓글로 부탁드리겠습니다^^
편의상 본문은 평어로 서술합니다 😇

https://youtu.be/-9X965jXrn8

앞 챕터에서 IL2CPP의 변환과정인 C#코드를 C++로 변환하는 과정에 대해서 알아보았는데,

성능 측면에서 문제가 되는 경우가 몇가지 있다! 그 중 대표적인것이 'Generic Sharing' 과정!

 

                                  === C# Generic ===
public class GenericClass <T>
{
    T item;
    
    public void UpdateItem(T newItem)
    {
        item = newItem;
    }
}

public class GenericClassExample : MonoBehaviour
{
    void Start()
    {
    	GenericClass<int> myClass = new GenericClass<int>();
        myClass.UpdateItem(5);
    }
}
                               === C++ Template ===
template <typename T>
T sub(const T& _a, const T& _b)
{
    return _a < _b ? _a : _b;
}
...

int a = 1;
int b = 2;
int i = minimum<int>(a, b);

...

float a = 1;
float b = 2;
float i = minimum<float>(a, b);

int sub(const int& _a, const int& _b)
{
    return _a < _b ? _a : _b;
}

float sub(const float& _a, const float& _b)
{
    return _a < _b ? _a : _b;
}

C++의 template의 경우

위 코드에서와 같이 코드는 간단할지 몰라도 컴파일러가 분석시 해당 자료형을 하나하나 만들어낸다.

즉, int면 int, float이면 float 같이 사용된 타입에 따라서 해당 버전들을 따로 만들어 낸다는 뜻!

다른말로 타입을 많이 사용하면 할수록 코드가 길어질 수 있다.

결론적으로 코드 작성은 간단하지만, 코드 사이즈가 늘어난다는 단점이 있다!

 

Q. C# Generic과 C++ Template의 차이
-> Call by Value(데이터를 복제해서 넘김) vs Call by Reference(데이터의 위치 정보를 넘김)
                                    === C++ ===
Struct A
{
    int a, b, c, d, e, f;
}
...

A a;
func(a)
=== 24 byte 데이터가 넘어감 (Call by Value) ===

A* a;
func(a)
=== 8 byte 주소값 데이터가 넘어감 (Call by Reference) ===

A a;
func(&a);
=== 8 byte 주소값 데이터가 넘어감 (Call by Reference) ===
                                   === C# ===
int i = 10;
String s = "Handsome OZ"
...
func(i, s);

=== i는 Value타입, s는 Reference타입 ===

/*
Value type : struct, int, char, bool ...
Reference type : class, string, arry ...
*/

=== C#은 Call by Value를 사용하냐 Call by reference를 사용하냐는 어떤 데이터형을 사용하는지에 따라 자동적으로 결정된다. ===


C#의 Generic이 C++로 변환되면 Generic은 굉장히 길게 풀어서 적어야한다. (IL2cppOutput 폴더에서 확인가능)
(Generic의 Call by Reference의 경우 Gereric을 하나로 만들어서 공유할 수 있다. 이것이 Generic Sharing)
때문에 C#에서 Generic을 많이 사용하면 할수록 IL2CPP 컴파일 과정이 오래걸리게 된다.

- Faster runtime performance :

Reference type 제너릭은 공유 코드 사용!

Value type이면 코드 복제본을 만들어서 코드 사이즈가 늘어나는 과정을 실시해서 컴파일 과정이 오래 걸리게 된다.

알 수 없는 Value type 제네릭이 발견되면 공유 코드 사용(기존 방식)

 

- Faster(smaller) builds :

Call by Value 타입 제네릭도 항상 공유 코드 사용! (빌드 시간 및 사이즈 감소)

다시말해, Call by Value 타입도 pointer로 싸서 하나의 공유 코드로 퉁쳐서 사용할 수 있게 한다!

결론적으로 코드 사이즈를 절약하고, 그로인해 빌드 사이즈도 줄어든다.

다만 pointer로 받아가서 type을 추론하고 추론한 것을 다시 원래 데이터로 복구하는 과정이 추가적으로 들어가서 런타임 성능이 줄어든다.

 

결론적으로 개발과정에서는 Faster(smaller) builds를, 최종 배포에서는 Fater runtime performance 사용 권장! (Unity 2022버전)

두 가지 선택지는 프로젝트의 크기가 클수록 빌드 속도차이가 많이 나는것을 체감할 수 있을 것이다!

 

번외:)

https://yarujaneka.tistory.com/m/6

 

런타임(Runtime) vs 컴파일타임(Compiletime)

프로그램을 돌리기 위해서 개발자가 첫째로 해야 할 일은 '소스코드 작성'을 하고 컴파일(Compile)이라는 과정을 통하여 기계어 코드로 변환되고 실행된다. 이러한 편집 과정을 컴파일타임(Compileti

yarujaneka.tistory.com

 

반응형