✍개요
C# 혹은 Unity를 많이 사용하셨던 분들은, 미들웨어 및 닷넷 프레임워크(.NetFramework)에 대해서 익히 알고 계실 겁니다. 유니티는 게임 콘텐츠 작성에 C#을 사용하고 있기 때문이지요. 즉 Unity에서의 닷넷은 MS의 닷넷과 다릅니다. 이 때문에 프로그래머는 이를 잘 숙지하고 있어야합니다,특히 엔진 개발자라면 더욱 더 말이죠. C++과 다르게 메모리관리를 자동으로 하는 C#이기에, 이를 잘 숙지해야 메모리관리를 능숙하게 할 수 있기 때문입니다.이에 Unity .NET의 구조를 보면서 어떻게 다른지 전반적으로 알아보려고합니다.
📌유니티가 사용하는 .NET (스크립트, 백엔드)
유니티에서 사용하는 .NET은 두가지 종류가 있습니다. 각각 MONO와 IL2CPP입니다. 차이점을 간략하게 정리하면 아래와 같습니다. 참고로 MONO는 32비트를 지원하고, IL2CPP 성능면에서 빠르기에 선호도가 낮습니다. 근데 ILCPP도 단점은 있습니다. 바로 AOT방식이라 최종 빌드되는 앱의 크기가 증가하게 되고, 이는 곧 빌드시간이 늘어나게 된다는 점 입니다. 따라서 프로젝트마다 다르지만, 개발 중에는 Monoo를 사용하여 빌드시간을 줄이고 디버깅을 용이하게 하며, 최종빌드에서 IL2CPP로 넘어가는 경우도 있습니다.이렇게 시간자원과 공간자원을 동시에 해결하는건 흔치는 않지만 결국엔 이런 메모리적 문제도 고성능 컴퓨터로 해결하는 경우도 많습니다.
유니티에서 사용하는 .NET : MONO, ILCPP
Mono :
• 스크립트 직접 실행 및 빠른반복 시간
• 디버깅에 조금 더 친숙합니다.( 오리지널 소스코드와 런타임 코드가 직접적으로 매핑됩니다. IL2CPP는 C++로 변환하기에 매핑이 따로 필요하죠.)
• 자바처럼 크로스 플랫폼 대응
• JIT(Just-In-Time) 컴파일 이용
-기타
• 현재는 MS가 인수하여 관리
• 현재 Xamarin. (물론 이것도 MS가 인수했습니다)
IL2CPP:
• Mono는 32비트를 주로 대응하기에 64비트를 대응하기 위해 제작
• AOT(Ahead-Of-Time) 컴파일 지원
• 성능 최적화 : IL(intermediate Language)코드를 C++로 변환한 후 컴피일하는 방식이라 높은 성능을 제공합니다.
📌IL2CPP를 이용한 빌드과정
*C# by Roslyn --> IL, IL Stripping --> C++, C++ in a target platform--> exe or dll file
*
위와 같이 요약할 수 있는데 세부사항을 정리하면 아래와 같습니다.
• 1. Roslyn **컴파일러가 C# 코드를 컴파일 합니다.
(참고로 Roslyn은 C#을 컴파일 하기 위해 사용하는 플랫폼과 같은 개념으로, 대체로 Roslyn을 사용합니다.)
• 2. IL2CPP가 IL Stripping(Intermediate Language Stripping)을 실시해 애플리케이션의 크기를 줄입니다.
• 3. IL2CPP가 **IL 코드를 표준 C++ 코드로 변환합니다.
• 4. C++ 컴파일러가 변환된 C++ 코드를 타겟 플랫폼으로 컴파일 합니다
•5. Unity가 타겟 플랫폼에 따라 실행 파일 혹은 DLL을 생성하게됩니다.
참고로 코드 스트리핑은 사용되지 않는 코드를 삭제하는 것입니다. 여기서 주의할 점은 코드 스트리핑 때문에 원치않게 버그를 맞이할 수도 있다는 점 입니다. 대표적으로는 동적 맴버변수를 활용하여 클래스를 참조하거나 할 때 사용하지 않는 맴버나 클래스로 인식하여 의도치 않게 코드 스트리핑 대상이 되는 경우가 있습니다.
📌라이브러리
Unity는 C#을 사용하지만, 최신 .NET 라이브러리는 지원하고 있지 않습니다. 또한 모든 라이브러리를 지원하는 것도 아닙니다. 사용가능한 버전은 .NET Standard 2.0과 .NET 4.X 입니다. 다만 Unity가 권장하는 버전은 .NET Standard 2.0 입니다. 라이브러리 크기가 작아 실행 파일 크기도 줄이고, 크로스 플랫폼 지원도 원활하며, .NET 런터임도 지원하기 때문입니다. 컴파일 타임에 오류 또한 잡아냅니다. 하지만 이는 플랫폼과 Third Party Library에 따라 달라질 수 있습니다.
참고) A third-party library is a reusable software component that is developed and maintained by an entity other than the original vendor of the development platform)
📌Unity.NET의 메모리 관리계층
Unity 내에서 메모리관리를 크게 세가지 범주로 나누면 각각 네이티브 메모리, 관리되는 메모리 (Managed Memory), 관리 되지 않는 메모리(Unmanaged Memory)가 있습니다. 세가지 범주를 염두해서 각종 메모리 누수, 최적화등에 좀 더 정량적으로 다가갈 수 있을 것입니다.메모리 관리계층까지 설명하고, GC에 대한 심도있는 내용은 다음 포스팅에서 다룰 예정입니다.
네이티브 메모리
Unity 엔진이 내부적으로 사용하는 메모리 관리 시스템을 말합니다. 대부분 유저가 직접적으로 접근 할 수 없습니다.물론 Resource.UnloadUnusedAssets(), Destroy()등을 사용해서 리소스를 해제할 수 는 있지만 일반적으로 유저가 다루지는 않습니다. 유저가 직접적으로 접근하거나 조작할 수는 없는데, 여담으로 이는 언리얼이 오픈소스여서 접근 가능한 것이랑 상반되는 부분이긴 합니다. 다만 메모리를 미세조정 하고싶다면 Unity의 C# API를 활용해 간접적으로 수정은 가능합니다. 이 네이티브 메모리에는 씬, 에셋, 그래픽 API, 그래픽 드라이버, 서브시스템, 플러그인 버퍼 등이 저장되어있습니다.
관리되지 않는 메모리
직전에 말씀드린 네이티브 메모리에 접근해 메모리 할당을 미세 조정하기 위한 계층을 말합니다. 여기서 잡 시스템(Job System)과 버스트(Burst) 컴파일러를 위해 사용하게 됩니다. 잡 시스템과 버스트를 설명하기엔 내용이 조금 방대해지기 때문에 링크로 대체하겠습니다.
Unity Manual: Job System
Unity Manual: Burst
관리되는 메모리
가비지 컬렉터에 의해서 관리되는 메모리입니다. 일반적인 게임로직이나, Monobehaviour 스크립트 등이 이 영역을 사용합니다.이전에 살펴본 것 처럼, 메모리 할당과 해제시 일어나는 다양한 실수를 방지 할 수 있지만, 가비지 컬렉션을 제대로 이해하고 있지 않으면 성능저하가 일어날 수 있습니다.GC 호출은 프레임 드랍을 발생시키므로 최소화 해야 하거든요. Unity의 가비지 컬렉션은 MS의 방식과는 다릅니다. Unity는 Boehm GC방식을 사용하고 있습니다. 또한 비세대 방식이며 메모리의 압축 또한 일어나지 않습니다. 이는 Unity에서 메모리 단편화가 자주 일어나 힙이 쉽게 확장되고, 최적화 이슈가 발생할 수 있다는 것을 의미합니다. 그리고 가비지 컬렉션아 너무 많은 성능자원이 소모되는 것을 방지하기 위해 조금씩 가바지 컬렉션을 나누어 하는 증분 방식 또한 채택하여 사용하고 있습니다.!
참고) 빈공간이 연속적이지 않아 할당이 불가능한 경우를 단편화라고 하며, 이 경우 힙의 확장이 일어나게 됩니다.
감사합니다.
'Unity > Unity Overall' 카테고리의 다른 글
오리를 클릭하면 도망갔다가 돌아오는 로직 만들기 (feat. 닷트윈(DoTween)) (1) | 2023.10.26 |
---|