· 11 min read

Unity 6.7 vs Unity 7: IL2CPP와 CoreCLR

MatterKit과 Spill로 잰 Editor, Player, 빌드

같은 프로젝트를 Unity 6.7(6000.7.0b2)과 Unity 7 알파 9(7000.0.0a9)로 열고 같은 작업을 시켰다. 대상은 Tessel Works의 물리·파단 라이브러리 MatterKit이다. 연산과 할당이 많고 여러 스레드에서 돌아서 런타임 차이가 잘 드러난다. 실제 게임 Spill로도 확인했다.

Unity 7 alpha 9 vs Unity 6.7 at a glance

Time ratios: below 1× means Unity 7 is faster. Windows, MatterKit. Purple: Unity 7 wins

Editor C#
0.47–0.63× (Unity 7 wins)
time vs 6.7, Release and Debug
Script edit to reload
0.94–1.10×
unchanged (Mac 1.34–1.67×)
Editor memory
+283 MB
per Play entry, Entities
CoreCLR Player, long runs
0.82–1.02× (Unity 7 wins)
time vs Unity 7 IL2CPP
CoreCLR Player, first run
1.37–1.70×
slower: JIT compiles first
CoreCLR clean build
40 s (Unity 7 wins)
same as 6.7 Mono; IL2CPP takes 168–178 s
한눈에 보는 결과

Editor 안 C#은 2배 가까이 빨라졌다. Player는 오래 돌면 CoreCLR이, 처음 한 번은 IL2CPP가 앞선다. 이 글의 막대는 모두 시간 비다. 1보다 작으면 Unity 7 쪽이 빠르다.

어떻게 쟀나

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
00K00KMIT

직접 만든 unity-bench(v0.7.0, MIT)로 쟀다. 비교 대상마다 새 프로세스를 띄우고 순서를 번갈아 섞었다. 프로세스별 중앙값을 낸 뒤 그 값들의 중앙값과 범위를 비교했다. 핵심 항목은 프로세스 6개, 나머지는 2~4개다.

Windows (CI 러너 PC)Mac
CPUAMD Ryzen 9 7900X3D, 12코어 24스레드Apple M4 Max, 16코어(성능 12, 효율 4)
메모리64 GB DDR5-480048 GB
저장 장치NVMe SSD (Samsung PM9A1 2 TB)내장 SSD
GPUGeForce RTX 4080M4 Max 내장
OSWindows 11 Pro 25H2 (26200), 고성능 전원macOS 26.5.2
Unity6000.7.0b2, 7000.0.0a9같음
  • Player 숫자는 Windows PC 한 대의 값이다. Mac에서는 Editor와 빈 프로젝트 측정만 했다.
  • 기술 통계이지 유의성 검정이 아니다. 5% 안팎의 차이는 방향만 본다.
  • Editor는 batchmode로 쟀다. 창이 있는 Editor의 체감은 재지 않았다.
  • 6.7과 7은 기본 라이브러리가 달라 같은 입력에서도 물리 결과가 2~4% 갈린다.

Editor: C#은 2배 가까이 빨라졌다

Unity 7 Editor는 Mono가 아니라 CoreCLR(.NET 10) 위에서 돈다.

Editor: Unity 7 alpha 9 / Unity 6.7 time ratio

Below 1, Unity 7 is faster. Log axis. MatterKit, median of 3 to 6 processes

TaskWindowsMac M4 Max
60 C# cases, Release0.470.30
60 C# cases, Debug0.630.55
Enter Play Mode0.730.91
Open project, no Library0.900.80
Core API edit to reload1.041.56
Exit Play Mode2.773.44
Editor 작업별 시간 비
  • MatterKit의 순수 C# 시나리오 60개 중 55개에서 Unity 7의 가장 느린 프로세스가 6.7의 가장 빠른 프로세스보다 빨랐다. 7의 Debug가 6.7의 Release와 거의 같은 속도다.
  • Play 진입이 빨라진 것은 대부분 샘플이 Play마다 카탈로그를 굽는 C# 코드 몫이다.
  • Mac(M4 Max)은 C#이 더 빨라지고, reload는 오히려 1.3~1.7배 느리다.

Time per reload phase

After a comment edit in a Core script, ms. Unity 7 alpha 9. From Domain Reload Profiling in the Editor log

Unity versionUnload codeLoad assembliesFinalize, otherOtherTotal
Unity 6.7 (Mono)4845861,4571182,645 ms
Unity 7 (CoreCLR)8624291,3501082,749 ms
reload 한 번의 단계별 시간

reload가 그대로인 이유는 이전 코드를 내리는 단계다. Mono는 AppDomain을 통째로 버리고, CoreCLR은 collectible AssemblyLoadContext를 내린다. 이 단계가 484 ms에서 862 ms로 1.8배가 되어 어셈블리 로드에서 번 시간을 먹는다. TypeCache 스캔과 [InitializeOnLoad] 처리도 2배 안팎 느려졌다.

Player: 오래 돌면 CoreCLR, 처음 한 번은 IL2CPP

6.7 IL2CPP, 7 IL2CPP, 7 CoreCLR 세 가지로 빌드했다.

Player: time relative to Unity 6.7 IL2CPP

Below 1 is faster than 6.7 IL2CPP. Log axis. Windows, MatterKit, median of 3 to 6 processes

MeasureUnity 7 IL2CPPUnity 7 CoreCLR
Long runs
catalog: all 26 objects1.17×0.95×
table: frame median1.03×0.97×
4 perfume bottles at once0.97×0.64×
4 piggy banks at once1.00×0.78×
1 piggy bank1.00×1.81×
Right after launch
Launch to first frame1.03×1.53×
First spawn1.28×2.19×
First cut2.07×2.82×
Longest frame1.21×2.04×
Player 시간 비
  • 7 IL2CPP는 6.7과 거의 같다. 첫 자르기 2.07배와 catalog 1.17배는 아래 함정의 Vector3 문제다.
  • 7 CoreCLR은 오래 도는 작업에서 대체로 빠르고, 병을 여러 개 동시에 깰 때 차이가 가장 크다. 예외도 있다. 저금통 하나는 1.8배, 집 모형 자르기는 3분을 돌려도 1.5~1.7배 느리다.
  • 처음 한 번은 CoreCLR이 확실히 느리다. 처음 실행되는 코드를 그때 JIT로 컴파일한다. Unity 7은 .NET 기본값(tiered compilation, Dynamic PGO)을 그대로 쓰고, ReadyToRun 어셈블리는 System.Private.CoreLib 하나뿐이다. 출시 빌드라면 로딩 중에 주요 코드 경로를 한 번씩 돌려 두면 줄일 수 있다. 자세한 것은 CoreCLR은 왜 오래 돌면 빠른가에 적는다.

여러 스레드가 함께 할당하면

IL2CPP의 Boehm GC는 Unity 빌드에서 스레드별 할당 버퍼가 없다. 그래서 객체 하나를 만들 때마다 프로세스 전역 잠금을 잡는다. CoreCLR은 스레드마다 버퍼가 있어서 포인터만 민다.

Allocation throughput by thread count

Each thread allocates 32-byte objects. Million per second. Mac M4 Max, Release players, median of 7 processes

7 CoreCLR381 M/s, 1.9× of 1 thread

6.7 and 7 IL2CPP36 to 39 M/s, 0.84×

Allocation throughput by thread count
ThreadsUnity 6.7 IL2CPPUnity 7 IL2CPPUnity 7 CoreCLR
142.846.3196.9
240.942.9297.6
438.640.8396.6
836.038.9380.9
스레드 수별 할당 처리량
  • 할당을 32번에 한 번으로 줄여도 IL2CPP는 8스레드가 1스레드보다 느렸다(0.42배). 할당 없는 계산은 세 런타임 모두 7.5배 안팎으로 늘었다.
  • 병 깨짐에서도 보인다. 향수병 하나에서 넷으로 갈 때 시간이 IL2CPP는 2.7배, CoreCLR은 2.0배 늘었다.
  • 다만 MatterKit은 커터 스레드를 IL2CPP에서 4개, CoreCLR에서 8개 쓴다. 각자의 기본값 비교다.
  • 한 스레드에서만 할당하거나 Burst job처럼 관리 객체를 만들지 않는 코드에서는 차이가 거의 없다.

두 GC의 구조는 Boehm과 CoreCLR, 두 GC는 어떻게 다른가에 적는다.

빌드: IL2CPP만 느리다

Player build time

Seconds. Windows, MatterKit sample, median of 4 processes, first build of each excluded. 6.7 Mono measured separately on 10/23, alternating with 7 CoreCLR (42.6 s vs 41.3 s clean)

BuildBuild time
Clean build
6.7 IL2CPP178 s
7 IL2CPP168 s
6.7 Mono43 s
7 CoreCLR40 s (Unity 7 CoreCLR)
After editing one script
6.7 IL2CPP82 s
7 IL2CPP43 s
6.7 Mono21 s
7 CoreCLR21 s (Unity 7 CoreCLR)
Player 빌드 시간

클린 빌드는 CoreCLR이 40초, IL2CPP가 168~178초다. 그런데 6.7 Mono도 43초로 같다(번갈아 잰 따로 측정에서 42.6초 대 41.3초, 스크립트 수정 뒤 21.5초 대 20.7초). 빌드가 빠른 건 CoreCLR이라서가 아니라 C++로 변환하고 컴파일하는 단계가 없어서다.

Editor 메모리: Unity 누수는 고쳐졌고, Entities 경로는 남았다

Editor memory over repeated reloads

30 script edits, each followed by a reload. App Resident memory, Mac, small example project (not the MatterKit sample). Unity's own leak, fixed in alpha 9

Alpha 7 4.2 GB+122 MB per reload

Editor memory over repeated reloads
ReloadsUnity 7 alpha 7 (CoreCLR)Unity 7 alpha 9 (25 reloads)Unity 7 alpha 7 + two caches cleared (15 reloads)Unity 6.7 (Mono)
00.58 GB0.59 GB0.47 GB0.40 GB
10.70 GB0.61 GB0.54 GB0.42 GB
20.81 GB0.62 GB0.55 GB0.44 GB
30.93 GB0.62 GB0.55 GB0.44 GB
41.05 GB0.62 GB0.56 GB0.44 GB
51.16 GB0.62 GB0.56 GB0.44 GB
61.28 GB0.62 GB0.56 GB0.44 GB
71.40 GB0.62 GB0.56 GB0.44 GB
81.54 GB0.63 GB0.56 GB0.44 GB
91.67 GB0.63 GB0.56 GB0.44 GB
101.80 GB0.63 GB0.56 GB0.44 GB
111.90 GB0.63 GB0.56 GB0.44 GB
122.03 GB0.63 GB0.56 GB0.44 GB
132.14 GB0.63 GB0.56 GB0.44 GB
142.25 GB0.63 GB0.57 GB0.44 GB
152.37 GB0.64 GB0.57 GB0.44 GB
162.49 GB0.64 GB–0.44 GB
172.62 GB0.64 GB–0.44 GB
182.72 GB0.64 GB–0.44 GB
192.85 GB0.64 GB–0.44 GB
202.95 GB0.64 GB–0.44 GB
213.09 GB0.66 GB–0.44 GB
223.18 GB0.57 GB–0.45 GB
233.32 GB0.57 GB–0.45 GB
243.44 GB0.57 GB–0.45 GB
253.55 GB0.57 GB–0.45 GB
263.67 GB––0.45 GB
273.80 GB––0.45 GB
283.92 GB––0.45 GB
294.04 GB––0.45 GB
304.16 GB––0.45 GB
reload 반복 중 Editor 메모리

그림은 더 작은 예제 프로젝트의 값이다. 알파 7만 reload마다 늘고(+122 MB), 알파 9는 6.7처럼 평평하다.

  • 알파 7은 reload마다 이전 코드를 붙잡았다. MatterKit 샘플은 한 번에 230 MB씩 늘어 30번 만에 8.2 GB가 됐다. 원인은 Unity 내부 캐시 두 곳이었고, 알파 9에서 고쳐졌다. 릴리스 노트에는 없는 수정이다.
  • Entities를 쓰는 프로젝트는 알파 9에서도 domain reload를 켠 Play 진입마다 커진다. MatterKit 샘플은 한 번에 managed 힙 +102 MB, 프로세스 +283 MB다(Windows). Play 진입 때 domain reload를 끄면 늘지 않는다.

원인을 찾아간 과정은 Unity 7 Editor 메모리 누수를 쫓아서에 적는다.

실제 게임에서도 같았나: Spill

Spill은 유리병이 계단을 굴러 깨지는 물리 게임이다. Entities, Unity Physics, URP, 녹화 테이프 재생을 다 쓴다. 같은 PC, 같은 두 Editor로 하룻밤 6시간을 쟀다.

Spill vs MatterKit: same time ratios in a real game

Below 1 is faster. Log axis. Editor rows are Unity 7 / 6.7. Windows, same PC and editors

MeasureSpillMatterKit
Editor C#0.780.63
API edit to reload1.181.04
Player p50, 7 IL2CPP / 6.70.570.94
Player p50, CoreCLR / 7 IL2CPP0.770.92
Spill과 MatterKit의 시간 비
  • Editor는 같은 모양이다. 어셈블리 19개를 다시 컴파일하는 수정만 18% 느렸다. 이전 코드를 내리는 단계가 5.3배 걸렸다.
  • 메모리는 더 나빴다. Play 한 번에 +269 MB(MatterKit +283 MB)이고, 스크립트 reload마다 110 MB가 더 붙는다(MatterKit -5 MB). Mac에서는 패키지 두 곳(com.unity.pipeline, com.unity.ide.visualstudio)이 이전 코드를 붙잡았다. Windows 원인은 확인 중이다.
  • 7 IL2CPP가 43% 빨랐다. GC가 아니었다. MatterKit이 소리를 내려고 접촉을 샘플링하는 job 하나가 managed 코드로 돌았고, 6.7 IL2CPP에서 특히 느렸다. Burst로 바꾸면 차이가 거의 사라진다. Spill main은 이미 Burst가 기본이다.
  • CoreCLR은 오래 돌면 p99가 7 IL2CPP의 0.39배다. 실행에서 첫 프레임까지는 1.67배 걸렸다. 클린 빌드는 6.7 IL2CPP 225초, 7 IL2CPP 250초, CoreCLR 90초다.

알아 둘 함정

재현과 원인은 Unity 7 IL2CPP에서 밟은 함정들에 적는다.

  • Unity 7 IL2CPP의 System.Numerics.Vector3: 같은 계산이 6.7 IL2CPP의 4.7배 걸린다. 알파 9에서도 그대로다.
  • 6.7 Editor(Mono)의 float: double로 계산해서 Math.Floor(-0.17f / 0.005f)가 Player와 한 칸 다르게 나온다.
  • 알파 9 IL2CPP 링커: 빌드에서 뺀 어셈블리도 [AlwaysLinkAssembly]가 붙어 있으면 링크한다. Spill의 7 IL2CPP 빌드가 이것으로 실패했다.
  • IL2CPP 종료 crash: 스레드 풀 작업이 도는 중에 끝내면 6.7과 알파 9 모두 죽는다(Spill 20번 중 6번).

그래서 무엇을 쓸까

PC 게임에서 IL2CPP를 고른 가장 큰 이유는 구형 Mono보다 빨라서였다. Unity 7의 CoreCLR은 그 Mono 자리를 현대 .NET으로 바꾼다. PC 출시 백엔드로 CoreCLR을 진지하게 볼 이유가 생겼다.

CoreCLR or IL2CPP for a PC release

Concept diagram of where each backend wins in this benchmark. Unity 7 alpha 9, so a direction, not a verdict

CoreCLR

  • Long game loops mostly faster
  • Clean builds about 4× faster than IL2CPP (same as Mono)
  • Scales when many threads allocate

IL2CPP

  • First run of new code is faster
  • Native code, harder to decompile
  • Required on consoles and iOS (no JIT)

Either way, physics results differ slightly by runtime. Bake replays and data with the one you ship.

CoreCLR과 IL2CPP가 각각 앞선 점

알파 9 기준이라 결론이라기보다 방향이다. Spill은 출시까지 반년 이상 남아 Unity 7으로 올린다. 백엔드는 출시 전에 같은 방법으로 다시 재서 정한다.

이어지는 글