7P by GN⁺ 10시간전 | ★ favorite | 댓글 8개
  • Unity가 사용하는 Mono 런타임은 최신 .NET 대비 현저히 느린 실행 속도를 보이며, 동일한 C# 코드가 최대 15배까지 차이 나는 사례가 있음
  • 실제 게임 코드에서 Mono 기반 Unity 실행은 100초, 동일 코드의 .NET 실행은 38초로 측정되어, 디버깅과 테스트 효율에도 큰 영향을 줌
  • Release 모드에서도 Mono는 30초, .NET은 12초로, 최적화된 환경에서도 2.5배 이상 성능 차이가 유지됨
  • 원인은 Mono의 비효율적인 JIT 컴파일과 인라이닝 실패, 메모리 복사 과다 등으로, .NET의 최신 CoreCLR JIT 최적화와 대비됨
  • Unity가 CoreCLR 기반 .NET 현대화를 완성하면 게임과 에디터 모두에서 큰 성능 향상이 가능하며, 이는 모든 Unity 프로젝트의 숨은 성능세 제거로 이어질 전망

Unity의 Mono 사용 배경

  • Unity는 2006년부터 Mono 프레임워크를 사용해 C# 코드를 실행
    • 당시 Mono는 유일한 멀티플랫폼 .NET 구현체였으며, 오픈소스로 Unity가 수정 가능
  • 2014년 이후 Microsoft가 .NET Core를 오픈소스로 공개하고, 2016년 .NET Core 1.0을 출시
    • 이후 Roslyn 컴파일러, 새 JIT, 성능 개선 등 .NET 생태계가 빠르게 발전
  • 2018년 Unity 엔지니어들은 CoreCLR 포팅 작업을 진행 중이라 밝히며, Mono 대비 2~10배 성능 향상을 기대
  • 그러나 2025년 말 현재까지도 CoreCLR 기반 게임 실행은 불가능한 상태

Mono와 .NET의 성능 격차

  • Unity 프로젝트의 시뮬레이션 코드를 Unity 외부에서 .NET으로 실행해 직접 비교
    • Unity/Mono 환경: 100초, .NET 환경: 38초 (Debug 모드 기준)
  • Release 모드에서는 Mono 30초, .NET 12초로 차이가 유지
    • .NET은 4K×4K 맵을 3초 내 생성하는 등 멀티스레드 최적화가 뛰어남
  • Mono의 비효율적 코드 생성이 주요 원인으로, 단순 루프에서도 15배 속도 차이 발생

어셈블리 비교: Mono vs .NET

  • 동일한 테스트 코드로 생성된 x64 어셈블리 비교 결과
    • .NET JIT은 루프 불변식을 루프 외부로 이동(hoisting) , 최소한의 레지스터 연산만 수행
    • Mono는 수십 개의 mov 명령어로 메모리 복사를 반복하며, 비효율적 인라이닝으로 성능 저하
  • int.MaxValue 반복 루프 실행 시간
    • .NET: 750ms, Mono: 11,500ms, Unity Editor(Debug): 67,000ms
  • Mono는 루프 내에서 불필요한 메모리 이동과 비교 연산을 반복 수행

CoreCLR 도입의 의미

  • CoreCLR은 최신 JIT, Span<T> API, SIMD 최적화, 하드웨어 명령어 지원 등 현대적 기능 제공
    • 이러한 기능은 추가적인 2배 이상 성능 향상 가능성
  • Unity의 Burst 컴파일러는 LLVM 기반으로 네이티브 코드를 생성하지만, C# 기능 제한이 존재
    • CoreCLR의 현대적 JIT은 Burst와 유사한 성능을 제공하면서 언어 제약이 적음
  • CoreCLR은 AOT(사전 컴파일) 지원으로 시작 속도 개선JIT 제한 플랫폼(iOS 등) 대응 가능
    • 그러나 Unity는 여전히 IL2CPP 유지 방침을 밝힘

결론: Unity의 .NET 현대화 필요성

  • Mono는 최신 .NET 대비 1.5~3배 이상 느린 실행 성능을 보이며, 이는 모든 Unity 프로젝트의 숨은 비용으로 작용
  • CoreCLR 도입 시 기대 효과
    • 런타임 성능 향상, 빠른 반복 빌드, GC 개선, 도메인 리로드 제거, 관리 코드 비중 확대
  • Unity 6.x 로드맵에 .NET Modernization이 포함되어 있으나, 2026년 이후로 예정
  • CoreCLR 지원이 완성되면 Unity 개발자와 플레이어 모두에게 실질적 성능 혁신 제공 가능
  • 현재로서는 Mono의 한계가 Unity 생태계 전체의 성능 병목으로 남아 있음

NuGet 호환도 추가해주면 좋겠어요(유니티를 잘 몰라서 그런걸까요?)

이론 상으로 보면 .NET Standard 2.0을 타겟으로 맞추어진 Nuget 패키지들은 Unity 환경에서도 불러와서 쓸 수 있게 되어있..긴 한데 아무래도 불편한 점이 많은 것 같긴 합니다.

https://learn.microsoft.com/ko-kr/dotnet/…

맞말이긴한데 굳이 에디터 퍼포먼스를 비교하는 이유는 잘 모르겠군요... 하다못해 디버그 빌드라도 한걸 들고와서 비교했으면? 아닌가 그럼 설득력이 더 떨어졌나? 한편으로는 IL2CPP나 Mono나 시대에 뒤떨어진 기술인건 매한가지같긴한데

큰 프로젝트에서는 에디터 성능이 개발 경험을 크게 깎아먹고 있어서 에디터 성능도 의미가 있습니다. 에디터 켜지는 게 느리고, 에셋 임포트도 느리고, 디버깅/테스트 루프도 느리고...

Mono가 반드시 CoreCLR로 현대화되어야 할 또 다른 이유로, Unity는 Mono의 성능 개선에 투자할 여건이나 의지가 높지 않을거라는 데에 있다고 생각합니다. .NET Framework 시절의 유산이 하루라도 빨리 정리되는게 맞다고 생각합니다. :-D

Unity는 Mono의 성능 개선에 투자할 여건이나 의지가 높지 않을거

이 또한 격하게 동의합니다...

그리고 .NET 10을 기점으로 과거 IL2CPP로 그들이 해결하고 싶어했던 문제가 다른 전개 방향이지만 정확히 다루어지고 있는 점 (Native AOT)도 고려가 되면 좋겠단 생각이 듭니다.

물론 이는 중간에 편집 가능한 C++ 코드가 만들어지지 않는게 한계이긴 하나, 결과적으로 Just-In-Time이 발생하지 않는 네이티브 바이너리 프로듀싱이 .NET 8을 시작으로 10에 와서 더욱 성숙해졌습니다.

그런 이유로 CoreCLR로 현대화하는 것을 계속 미루는 것은 Unity에게 좋은 선택이 되지 못할 거라고 봅니다. 아니면 완전히 다른 언어나 기반으로 갈아타는 교체가 더 유효할 수도 있겠고요!

Hacker News 의견들
  • 전문 게임 개발자로서 Unity 관련 논의에는 보통 참여하지 않지만, 이번 주제는 내 전문 분야와 직접적으로 맞닿아 있어서 의견을 남김
    글에서 Unity 개발 경험이 적은 사람이 쓴 듯한 부분이 몇 가지 보였음
    1. 시뮬레이션과 표현 계층 분리는 과거부터 흔히 쓰이던 성능 최적화 방식이었음 (과거엔 주로 C++로 구현했지만)
    2. 대부분의 게임은 Mono 백엔드가 아닌 IL2CPP로 출시됨
    3. 최신 Unity에서는 성능을 위해 Burst Compiler와 HPC#을 활용하는 게 일반적임. Job System이 큰 도움이 됨
    4. 에디터에서 프로파일링하는 건 의미가 거의 없음. 디버그 빌드보다 훨씬 느리기 때문임
      요약하자면, Unity 개발자들이 이번 업데이트를 기대하는 이유는 성능 향상보다는 최신 언어 기능 접근성 때문임. 그리고 런타임 중 GC를 최소화하거나 비관리 메모리와 DOTS로 우회하는 게 일반적임
    • Unity의 마케팅에 너무 휘둘린 것 같음. Unity는 항상 문제 많은 시스템을 조금씩 개선하는 식으로 접근함
      IL2CPP는 .NET IL을 C++로 변환하는 품질 낮은 코드 생성기에 불과하며, 최적화 컴파일러에 의존함
      Unity 블로그의 IL2CPP 내부 구조를 보면 알 수 있음
      Burst/HPC#도 ECS나 SoA 같은 트렌드를 따르지만, 성능은 잘 짜인 C++이나 CoreCLR C#보다 떨어짐
      게다가 이 기술들은 폐쇄적이고 Unity 전용이라 외부에서 쓸 수 없음. Unity는 항상 느린 Mono와 비교한 벤치마크로 마케팅을 함
      결국 Unity도 CoreCLR을 받아들일 수밖에 없게 되었고, 그때가 되면 기존 복잡한 코드보다 일반 C# 코드가 더 빠르다는 현실을 깨닫게 될 것임
    • 글쓴이로서 의견에 감사함. 우리 게임은 Unity 타입이나 DLL에 전혀 의존하지 않는 구조라서 일반적인 Unity 게임과 다름
      IL2CPP를 쓰지 않는 이유는 런타임 DLL 로딩, 리플렉션, FieldOffset 구조체 패킹 등과 호환되지 않기 때문임
      모더들이 IL 인젝션으로 기능을 확장할 수 있어 결과적으로 개발 속도가 빨라짐
      Burst와 HPC#은 복잡성과 제약이 많아 선호하지 않음. Mono와 .NET의 성능 차이 때문에 더 답답함
      에디터 프로파일링도 실제 빌드와 비슷한 비율로 성능 향상을 보여서 유용했음. 대신 Unity의 기본 프로파일러는 부정확해서 직접 만든 추적 시스템을 사용함
      GC는 여전히 문제임. 문자열 처리나 UI가 매 프레임 가비지를 생성함. CoreCLR로 가면 더 나은 API와 이동식 GC로 메모리 단편화 문제를 줄일 수 있을 것임
  • Unity는 진입이 쉽지만 모놀리식 구조로 인해 기술 부채가 심함
    Asset Store는 훌륭하지만 엔진 자체는 다듬어지지 않은 느낌임.
    Mono 기반 스크립팅이 CoreCLR로 옮기기엔 구조적으로 복잡함
    Unity가 진정으로 Core를 개선하려면 Blender 3.x처럼 에디터 전체를 리디자인해야 함.
    현재는 1999년식 UI처럼 느껴짐
    • Unity의 가장 큰 문제는 새 기능을 완성하지 않고 버린다는 점임.
      수많은 플러그인과 툴이 “0.x-preview” 단계에서 멈추고, 5~10년 후엔 작동하지 않거나 새 자산에 묻힘
      그래서 나는 이제 1.0 이상 버전만 사용함. 그렇지 않으면 버려진 플러그인에 의존하게 되어 결국 다시 포팅해야 함
      Unity도, 개발자도, 사용자도 모두 손해임
    • Unity는 방향성을 잃은 회사처럼 보임.
      내부적으로 자체 게임 개발 실패로 인해 실제 게임 제작의 감각이 부족함
      단순히 요청받은 기능을 추가할 뿐, 일관된 비전이 없음
    • WebGPU는 Vulkan 같은 추상화 계층임.
      성능이 중요하면 Vulkan을 직접 호출하는 게 낫고, 이식성이 중요하면 WebGPU를 써야 함
      브라우저마다 구현이 달라서 오버헤드가 생기지만, OS 드라이버 수준에서 WebGPU를 제공하면 해결 가능함
    • Unity는 뭔가를 하려면 항상 우회 방법을 찾아야 하는 느낌임
      반면 Godot은 단순하고 의미 있는 빌딩 블록을 제공해서 원하는 걸 자유롭게 만들 수 있음
    • Unity는 자사 기능의 API를 자주 깨뜨려서 튜토리얼조차 작동하지 않게 만듦
      Asset Store도 마찬가지로 버전 호환성 문제로 인해 유지보수가 어렵고, 대부분 방치된 자산이 됨
      Unity가 유용한 자산을 사들이면 통합도 제대로 안 하고, 경쟁 자산은 사라짐
      반면 Unreal Engine은 이런 기능들을 엔진 내장 수준으로 제공함
  • Unity의 GC는 Boehm GC를 사용해서 .NET이나 Mono보다 훨씬 느림
    Unity가 IL2CPP에 더 나은 GC를 도입할 계획도 없음
    CoreCLR 기반 에디터가 나오면 오히려 에디터가 빌드보다 빠를 수도 있음
    관련 토론: Unity CoreCLR 및 .NET 현대화
    • GC가 좋아지는 건 좋지만, 게임에서는 프레임당 할당을 0으로 유지하는 게 더 중요함
      인크리멘털 GC가 잘 작동하면 스터터 문제도 크지 않음
    • CoreCLR 전환 시 도메인 리로드 제거로 에디터 속도가 크게 향상될 것임
    • Mono에는 이미 정확한 GC가 있는데 왜 Boehm을 쓰는지 의문임
  • Unity가 2018년에 CoreCLR 포팅을 논의했을 때부터 긴 여정이었음
    C#이 기본적으로 매우 빨라졌기 때문에, Unity는 반드시 이 전환에 모든 역량을 집중해야 함
    우리 팀은 .NET Framework 4.7.2에서 .NET 6으로 옮기는 데 몇 달 걸렸고, 이후 LTS 버전 업그레이드는 몇 시간 수준으로 간단했음
  • Unity는 더 이상 CoreCLR로의 마이그레이션을 수행할 기술 역량이 부족해 보임
    계속 지연되고, 리더들이 떠남
    대안으로 .NET 10 기반 Stride 엔진을 추천함. Unity처럼 경계 오버헤드가 없음
    • 진행은 느리지만 Unity는 실제로 CoreCLR 전환을 추진 중
    • Stride는 Unity 기능의 일부만 제공함.
      Godot은 오픈소스지만 C# 지원이 불안정하고, Web 빌드가 안 되면 게임잼용으로 부적합
      GPU 지원이 있는 진짜 샌드박스 솔루션이 필요함
    • 기술력 부족이 아니라 리소스와 리더십 문제
      우선순위가 계속 바뀌고, 요구사항이 수정되면서 작업이 반복됨
      개발자들은 여전히 뛰어나지만, 일관된 추진력이 부족함
    • Unity가 인수한 수많은 기술과 애드온을 폐기하거나 통합해야 하는 부담이 큼
      이런 대규모 리라이트는 CEO 입장에서도 리스크가 큰 결정
  • Rec Room의 Circuits 시스템을 개발했는데, Unity 의존성을 제거하고 CoreCLR에서 테스트 가능하게 만든 경험이 있음
    그 결과 성능이 크게 향상되었고, 엔진 종속성 감소로 코드 유지보수도 쉬워졌음
    Unity 개념을 필요한 곳에만 노출하고, 테스트로 경계를 강제함으로써 논리적 분리의 가치를 체감했음
  • 왜 글쓴이가 IL2CPP 대신 Mono를 쓰는지 궁금함. IL2CPP가 훨씬 빠르기 때문에 Mono 빌드는 구식임. 성능을 중시한다면 IL2CPP가 첫 단계여야 함
  • 내 꿈은 .NET SDK를 안드로이드 폰에 직접 설치해서 네이티브로 실행하는 것임
    루트 접근이 가능하고, WireGuard나 Tailscale 같은 네트워크 툴과 결합하면 휴대용 서버로도 완벽함
    .NET 10의 새로운 GC로 게임 스터터도 거의 사라질 것임
    현재는 메인 PC에서 Sunlight + Moonlight로 게임을 스트리밍해서 폰에서 플레이함.
    고주사율 OLED 화면 덕분에 배터리 소모도 적음
    • 루트 제한은 보안상의 이유임. 공공장소의 USB 포트로 인한 감염을 막기 위해 루팅을 어렵게 만든 것임
    • MAUI(Xamarin)로 비슷한 걸 할 수 있음.
      .NET SDK는 아니지만 Mono 런타임이 앱에 포함되어 있어서 체감상 비슷함
  • Unity가 왜 여전히 .NET으로 완전히 전환하지 않는지 궁금함
    Mono의 크로스플랫폼 이점은 이미 사라졌는데, 왜 IL2CPP 같은 복잡한 해킹을 유지하는지 이해가 안 됨
    • 매몰비용의 함정이 큼. 이미 Mono와 IL2CPP, Burst 등에 막대한 투자를 했기 때문에 방향을 바꾸기 어려움
    • Unity는 Mono를 심하게 커스터마이징했기 때문에 단순히 교체할 수 없음
      수년간의 비표준 수정이 누적되어 있어서, 대기업이 아니라면 새로 최적화하기도 어려움
  • Unity가 2018년부터 CoreCLR 전환을 시작했는데 아직 완료하지 못한 게 놀라움
    이 정도 프로젝트라면 1~2년이면 충분했을 것이라고 생각함