08/21 Unity Profiler - 메모리 최적화, Draw Call 최적화, Object Pooling

댓글 0
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요.

댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요.
Profiler에서 확인할 3가지 핵심 지표
이번 단원 핵심 용어
| 용어 | 핵심 의미 |
|---|---|
| Profiler | 실행 중 게임의 CPU, GPU, Memory, Rendering 등의 성능을 측정하는 Unity 도구 |
| GC Allocation | C# 관리 힙에 새로운 메모리가 할당되어 이후 Garbage Collection 대상이 되는 현상 |
| Draw Call / Batching | GPU에 보내는 렌더링 명령과 여러 렌더링 작업을 묶어 호출 횟수를 줄이는 방식 |
| Static Batching | 움직이지 않는 오브젝트를 묶어 Draw Call을 줄이는 방법 |
| Dynamic Batching | 조건을 만족하는 움직이는 오브젝트를 런타임에 묶어 렌더링하는 방법 |
| GPU Instancing | 동일한 Mesh·Material을 사용하는 다수의 오브젝트를 효율적으로 한꺼번에 렌더링하는 방법 |
| LOD Group | 카메라와의 거리에 따라 더 단순한 Mesh로 자동 전환하여 렌더링 비용을 줄이는 Component |
| Object Pooling | 오브젝트를 반복 생성·삭제하지 않고 미리 생성한 뒤 재사용하는 패턴 |
| 감지 주기(Tick Rate) | AI가 주변 상황을 감지하고 판단하는 실행 간격. 매 프레임 실행할 필요는 없음 |
Unity Profiler

최적화를 할 때, 프로파일러에서는 대표적으로 다음 3가지를 본다.
상단 그래프 영역
Profiler의 상단 Module Chart 영역은 게임을 실행하면서 성능 지표가 시간에 따라 어떻게 변하는지 전체적인 흐름을 관찰하는 곳입니다. 가로축은 프레임의 진행을 나타내며, 그래프가 갑자기 위로 튀는 지점을 선택하면 해당 프레임을 하단 Details Pane에서 자세히 분석할 수 있습니다. 따라서 상단에서는 “언제 문제가 발생했는가?”를 찾고, 하단에서는 “왜 문제가 발생했는가?”를 찾는 구조라고 이해하면 좋습니다.
| Module | 확인하는 내용 | 주요 활용 |
|---|---|---|
| CPU Usage | 한 프레임을 처리하는 데 걸린 CPU 시간 | CPU Spike, 느린 프레임 탐색 |
| Rendering | SetPass Calls, Batches, Triangles, Vertices 등 | Draw Call 및 렌더링 부하 확인 |
| Memory | 게임이 사용 중인 메모리 변화 | 메모리 증가, 할당 문제 확인 |
CPU Usage
| 항목 | 설명 |
|---|---|
| Rendering | 카메라 렌더링, 드로우콜, 오브젝트 표시 등 화면을 그리는 데 사용되는 CPU 작업입니다. |
| Scripts | Update(), Coroutine, 사용자 작성 C# 코드 등 스크립트 실행에 사용되는 CPU 작업입니다. |
| Physics | 충돌 검사, Rigidbody, Collider 등 물리 연산에 사용되는 CPU 작업입니다. |
| Animation | Animator, Animation Clip, 본 계산 등 애니메이션 처리에 사용되는 CPU 작업입니다. |
| GarbageCollector | 사용하지 않는 Heap 메모리를 탐색하고 해제하는 GC 작업에 사용되는 CPU 시간입니다. 값이 순간적으로 크게 증가하면 GC 스파이크를 의심할 수 있습니다. |
| VSync | 모니터의 화면 갱신 주기에 맞추기 위해 CPU가 대기하는 시간입니다. |
| Global Illumination | 간접광, 라이트맵 등 GI(Global Illumination) 관련 연산에 사용되는 시간입니다. |
| UI | Canvas 갱신, UI 레이아웃 및 그래픽 처리 등 Unity UI 처리에 사용되는 CPU 작업입니다. |
| Others | 위 항목으로 분류되지 않은 기타 엔진 작업 및 CPU 사용량입니다. |
Rendering
| 항목 | 설명 |
|---|---|
| Batches Count | 한 프레임에서 GPU에 전달되는 렌더링 작업 묶음(Batch)의 개수입니다. 값이 많을수록 CPU의 렌더링 명령 처리 부담이 커질 수 있습니다. |
| SetPass Calls Count | 셰이더나 머티리얼 등 렌더링 상태를 변경하기 위해 발생한 호출 횟수입니다. 일반적으로 적을수록 렌더링 효율이 좋습니다. |
| Triangles Count | 현재 프레임에서 렌더링되는 삼각형의 총 개수입니다. 값이 지나치게 높으면 GPU 부하가 증가할 수 있습니다. |
| Vertices Count | 현재 프레임에서 처리되는 정점(Vertex)의 총 개수입니다. 값이 높을수록 메시 처리 비용이 증가할 수 있습니다. |
Memory
| 항목 | 설명 |
|---|---|
| Total Used Memory | 현재 Unity 애플리케이션이 사용하고 있는 전체 메모리 사용량입니다. 텍스처, 메시, 오디오, 스크립트 객체 등 여러 메모리 사용량이 포함됩니다. |
| Texture Memory | Texture, Sprite, RenderTexture 등 텍스처 리소스가 차지하는 메모리입니다. 고해상도 텍스처가 많을수록 크게 증가할 수 있습니다. |
| Mesh Memory | 캐릭터나 맵 등의 Mesh 데이터가 차지하는 메모리입니다. 정점이나 삼각형 수가 많은 메시를 많이 사용할수록 증가합니다. |
| Material Count | 현재 메모리에 존재하는 Material의 개수입니다. 불필요하게 Material 인스턴스를 생성하면 개수가 계속 늘어날 수 있습니다. |
| Object Count | 현재 메모리에 존재하는 Unity Object의 개수입니다. GameObject, Component, Texture, Material 등의 객체가 포함됩니다. |
| GC Used Memory | C# 관리형 Heap에서 현재 사용 중인 GC 관리 메모리의 양입니다. 지속적으로 증가하면 불필요한 객체 생성이나 참조 유지 여부를 확인할 필요가 있습니다. |
| GC Allocated In Frame | 해당 프레임에서 새롭게 할당된 관리형 Heap 메모리의 양입니다. 매 프레임 값이 발생하면 GC가 자주 동작하여 GC 스파이크가 발생할 가능성이 높아질 수 있습니다. |
Unity Profiler CPU Usage 하단 패널
Unity Profiler의 CPU Usage 모듈 하단 패널은 선택한 한 프레임에서 CPU가 어떤 작업에 시간을 사용했는지 자세히 분석하는 영역입니다. 위쪽 그래프에서 프레임이 튀는 CPU Spike를 발견한 뒤, 아래 패널을 이용해 어떤 함수가 원인인지 추적합니다. 하단 패널의 표시 방식은 Timeline, Hierarchy, Inverted Hierarchy, Raw Hierarchy 네 가지가 있으며, 같은 CPU 데이터를 서로 다른 관점으로 보여줍니다.
| View | 무엇을 보여주는가 | 주로 사용하는 상황 |
|---|---|---|
| Timeline | CPU 작업을 시간 순서와 막대 길이로 표시 | 어느 시점에 어떤 작업이 오래 걸렸는지 확인, CPU Spike 탐색 |
| Hierarchy | 함수의 호출 구조와 실행 비용을 표 형태로 표시 | 어떤 함수가 느린지, Calls, GC Alloc 등이 많은지 확인 |
| Inverted Hierarchy | 호출 관계를 반대로 보여줌 | 비용이 큰 함수를 누가 호출하고 있는지 역추적 |
| Raw Hierarchy | 호출을 합쳐 정리하지 않고 원래 호출 구조에 가깝게 표시 | 동일 함수가 어떤 경로에서 호출됐는지 세부 분석 |
Timeline은 CPU 작업을 시간의 흐름대로 시각화합니다. 가로로 긴 막대일수록 해당 작업이 CPU를 오래 사용했다는 뜻입니다. Main Thread, Render Thread, Job Worker 등 여러 스레드의 작업 시점도 비교할 수 있어 프레임이 갑자기 느려진 원인을 처음 찾을 때 가장 유용합니다.
Hierarchy는 PlayerLoop → Update → EnemyAI.Update()처럼 부모 함수와 자식 함수의 호출 관계를 보여줍니다. Total, Self, Calls, GC Alloc 등을 확인할 수 있기 때문에 실제 최적화에서 매우 자주 사용합니다. 특히 GC Alloc으로 정렬하면 매 프레임 메모리를 할당하는 함수를 찾기 쉽습니다.
Inverted Hierarchy는 Physics.Raycast()처럼 특정 함수가 비싼 것을 발견했을 때 “이 함수를 누가 호출했는가?”를 거꾸로 추적하는 데 사용합니다. Raw Hierarchy는 같은 호출을 보기 좋게 묶지 않고 실제 호출 구조를 더 세밀하게 확인할 때 사용하므로 일반적인 분석보다는 깊은 디버깅에 적합합니다.
실전 분석 순서:
Timeline에서 CPU Spike 발견 → Hierarchy에서 느린 함수·Calls·GC Alloc 확인 → 필요하면 Inverted Hierarchy로 호출 원인 추적 → 코드 최적화 → Profiler로 재측정
일반적인 성능 분석에서는 Timeline과 Hierarchy 두 가지를 가장 많이 사용한다고 기억해 두시면 됩니다.
Deep Profile이란?
Deep Profile은 Unity Profiler에서 C# 스크립트 내부의 메서드 호출을 더 세밀하게 추적하는 기능입니다. 일반 Profiler보다 훨씬 자세하게 기록하기 때문에, 특정 Update()가 느린 것뿐 아니라 그 안에서 어떤 메서드가 실제 병목인지까지 확인할 수 있습니다.
| 항목 | 의미 |
|---|---|
| Total / Total % | 해당 메서드와 하위 메서드 실행 시간을 모두 포함한 비용 |
| Self / Self % | 해당 메서드 자체에서 직접 사용한 시간 |
| Calls | 해당 메서드가 호출된 횟수 |
예를 들어 EnemyAI.Update()의 Total %가 높지만 Self %는 낮다면, Update() 자체보다 그 안에서 호출되는 DetectPlayer()나 Physics.Raycast() 같은 하위 메서드가 실제 병목일 가능성이 높습니다. 반대로 Self %가 높다면 해당 메서드 내부 로직 자체가 무겁다고 볼 수 있습니다. 즉, 흔히 말하는 메서드 병목률은 Total %, Self % 등을 보고 판단합니다.
Total %가 높은 메서드 확인 → Self % 확인 → 하위 메서드 추적 → Calls 확인
특히 Calls가 지나치게 많다면 한 번 실행하는 비용은 작더라도 매 프레임 수백 번 반복되면서 전체 성능을 떨어뜨릴 수 있습니다.
Deep Profile은 거의 모든 C# 메서드 호출에 계측 코드를 추가하기 때문에 Profiler 자체의 오버헤드가 매우 커집니다. 따라서 Deep Profile을 켠 상태에서 측정한 프레임 시간은 실제 게임 성능과 다를 수 있습니다.
권장 사용법: 평소에는 일반 Profiler로 큰 병목을 찾고, 원인을 더 세밀하게 확인해야 할 때만 Deep Profile을 잠시 활성화하여 메서드 단위로 분석합니다.
핵심:
Total % = 하위 호출까지 포함한 영향, Self % = 해당 메서드 자체의 비용, Calls = 반복 호출 횟수로 기억하시면 됩니다.
메모리 진단 : Garbage Collector
GC(Garbage Collector)는 Heap 영역에 생성된 관리형 객체 중 더 이상 참조되지 않는 데이터를 찾아 메모리에서 해제하는 기능입니다.
게임 실행 중 new를 반복하거나 문자열, List, 배열 등의 임시 객체를 계속 생성하면 Heap 영역에 데이터가 쌓이고, GC가 많은 객체와 참조 관계를 한꺼번에 검사·정리하면서 GC 스파이크가 발생할 수 있습니다.
GC 스파이크가 발생하면 GC 작업이 특정 프레임에 집중되면 순간적으로 CPU 사용량이 증가해 프레임 드랍, 화면 끊김, 입력 지연 등의 현상이 발생할 수 있습니다.
자주 사용하는 객체나 컬렉션은 매번 새로 생성하지 않고 재사용하거나 오브젝트 풀링(Object Pooling)을 적용해야 하며, 더 이상 필요하지 않은 객체는 참조를 제거해 GC가 정상적으로 회수할 수 있도록 관리하는 것이 중요합니다.
※ GC를 발생시키지 않도록 하는 것이 아니라, 최대한 덜 발생하도록 재활용하는 것임
CPU / GPU 최적화
Draw Call은 CPU가 GPU에게 특정 Mesh를 특정 Material/Shader로 화면에 그리라고 전달하는 렌더링 명령입니다.
Draw Call 주요 단계
맵의 건물, 벽, 바닥처럼 게임 중 위치·회전·크기가 변하지 않는 오브젝트는 Static으로 설정하여 Static Batching의 대상이 되도록 할 수 있습니다. Static Batching은 이러한 오브젝트의 Mesh를 렌더링하기 좋은 형태로 묶어 처리하여 CPU가 GPU에 전달해야 하는 Draw Call 및 렌더링 명령의 부담을 줄이는 방법입니다. 단, 같은 Material을 사용하는 등 배칭 조건이 맞아야 효과적으로 묶일 수 있습니다.
※ 움직이지 않는 오브젝트에 한해서 Static을 설정해야 하며, 플레이 중 움직이거나 Transform이 변경되는 오브젝트에는 적합하지 않습니다.


오브젝트를 static으로 설정한 경우 Play 시 Combined Mesh로 변경된다.
Combined Mesh란?
Batching Static을 설정한 오브젝트는 Play 시 Unity가 여러 정적 Mesh를 Combined Mesh로 묶어 렌더링할 수 있습니다. 이를 통해 개별 오브젝트마다 Draw Call을 보내는 부담을 줄여 렌더링 성능을 최적화합니다.
2D Rendering 최적화 : Sprite Atlas를 활용한 Draw Call 감소
Sprite Atlas는 여러 개의 Sprite Texture를 하나의 큰 Texture로 묶어서 사용하는 방식입니다. 2D 게임에서는 Sprite마다 다른 Texture를 사용할 경우 Texture가 변경될 때마다 렌더링 Batch가 분리될 수 있는데, Atlas를 사용하면 여러 Sprite가 같은 Texture를 공유할 수 있어 Draw Call을 줄이는 데 도움이 됩니다.
캐릭터, 배경, 아이콘 등의 Sprite가 각각 다른 Texture나 Material을 사용하면 렌더링 도중 Texture 또는 Material을 계속 교체해야 하므로 Batch가 나뉘고 Draw Call이 증가할 수 있습니다. 오브젝트가 많을수록 이러한 상태 변경 비용도 커질 수 있습니다.
같은 화면에서 자주 함께 렌더링되는 Sprite들을 Sprite Atlas로 묶어 하나의 Texture를 공유하도록 구성하면 여러 Sprite를 같은 Batch로 처리하기 쉬워져 Draw Call을 줄일 수 있습니다.
LOD

LOD(Level of Detail)는 카메라와의 거리에 따라 오브젝트의 Mesh 복잡도를 단계적으로 낮추는 최적화 기법입니다. 가까이 있는 오브젝트는 디테일이 많은 Mesh를 사용하고, 멀리 있는 오브젝트는 정점과 삼각형 수가 적은 Mesh로 교체하여 GPU의 렌더링 부하를 줄입니다.
예를 들어 나무 하나를 다음처럼 구성할 수 있습니다.
가까움 → LOD 0 : 고품질 Mesh
조금 멀어짐 → LOD 1 : 중간 품질 Mesh
멀리 있음 → LOD 2 : 저품질 Mesh
매우 멀리 있음 → Culled : 렌더링하지 않음
현재 설정은 화면에서 오브젝트가 차지하는 크기(Screen Size)를 기준으로 LOD가 변경됩니다.
| 단계 | Screen Size | 동작 |
|---|---|---|
| LOD 0 | 60% 이상 | 가장 디테일한 Mesh 사용 |
| LOD 1 | 30 ~ 60% | 중간 품질 Mesh 사용 |
| LOD 2 | 10 ~ 30% | 저품질 Mesh 사용 |
| Culled | 10% 미만 | 오브젝트를 렌더링하지 않음 |
즉, 단순히 몇 m 떨어졌는지로 판단하는 것이 아니라 카메라 화면에서 오브젝트가 얼마나 크게 보이는지를 기준으로 LOD가 전환됩니다.
Mesh Renderer 등을 등록하는 곳입니다. 현재 스크린샷에서는 모두 List is Empty 상태이므로 아직 Mesh가 등록되지 않은 상태입니다.
LOD가 LOD 0 → LOD 1 → LOD 2처럼 바뀔 때 Mesh가 전환되는 방식을 설정하는 옵션입니다. 갑자기 Mesh가 바뀌면서 튀어 보이는 Popping 현상을 줄이기 위해 사용합니다.
| 옵션 | 설명 |
|---|---|
| None | 전환 효과 없이 다음 LOD Mesh로 즉시 변경됩니다. 가장 단순하고 비용이 적습니다. |
| Cross Fade | 이전 LOD와 다음 LOD를 일정 구간 동안 겹쳐서 점진적으로 전환합니다. LOD 변경이 자연스럽지만 약간의 추가 렌더링 비용이 발생할 수 있습니다. |
| Speed Tree | Unity의 SpeedTree 전용 LOD 전환 방식입니다. 나무의 형태가 단계적으로 자연스럽게 변화하도록 처리합니다. |
Occlusion Culling은 카메라 시야 안에 있더라도 벽이나 건물 등에 완전히 가려진 오브젝트는 렌더링하지 않는 최적화 기법입니다.
| 구분 | 기준 | 특징 |
|---|---|---|
| Frustum Culling | 카메라 시야 밖 | Unity에서 자동 적용 |
| Occlusion Culling | 다른 오브젝트에 가려짐 | Bake 필요 |
Window > Rendering > Occlusion Culling에서 Bake화면에 실제로 보이지 않는 오브젝트의 렌더링을 생략하여 Draw Call과 CPU/GPU 렌더링 부하를 줄일 수 있습니다.
※ 정리하면 Batching은 묶어서 그리기, LOD는 멀면 단순하게 그리기, Occlusion Culling은 안 보이면 그리지 않기입니다.
Window > Rendering > Occlusion Culling 창을 엽니다.Occlusion Area를 배치하여 카메라가 이동하는 영역을 지정합니다.
Occlusion Culling을 통해 Occludee Static이 설정된 오브젝트는 카메라 밖에 나왔을 때 그리지 않음.
Lightmap Bake는 움직이지 않는 오브젝트에 대한 조명과 그림자 계산 결과를 미리 텍스처에 저장해두는 방식입니다. 실시간으로 조명을 계속 계산하지 않고, 미리 계산된 Lightmap을 사용하기 때문에 실행 중 CPU/GPU의 조명 연산 부담을 줄일 수 있습니다.
보통 해당 오브젝트를 Contribute GI 대상으로 설정하고, Light를 Baked 또는 환경에 따라 Mixed로 설정한 뒤 Lighting 창에서 Bake를 진행합니다.
실시간 조명 계산량을 줄여 렌더링 성능을 향상시킬 수 있으며, 간접광이나 부드러운 그림자도 미리 계산할 수 있습니다.
※ 단점은 Lightmap 텍스처가 추가되므로 메모리와 빌드 용량이 증가할 수 있고, Bake된 오브젝트나 조명이 크게 움직이는 환경에는 적합하지 않습니다.
Object Pool은 자주 생성되고 제거되는 오브젝트를 미리 일정 수 생성해두고, 필요할 때 꺼내서 사용한 뒤 다시 반환하여 재사용하는 방식입니다. 반복적으로 Instantiate와 Destroy를 호출하지 않기 때문에 실행 중 오브젝트 생성/삭제에 따른 CPU 부하와 GC 발생을 줄일 수 있습니다.
보통 게임 시작 시 필요한 오브젝트를 Pool에 미리 생성해두고 비활성화 상태로 보관합니다. 사용 시 Pool에서 오브젝트를 가져와 활성화하고, 사용이 끝나면 Destroy하지 않고 다시 비활성화하여 Pool에 반환합니다.
반복적인 오브젝트 생성과 삭제를 줄여 CPU 사용량과 메모리 할당을 감소시키고, GC로 인한 프레임 드롭을 완화할 수 있습니다. 특히 짧은 시간에 많은 오브젝트가 생성되는 상황에서 효과적입니다.
※ 단점은 사용량보다 너무 많은 오브젝트를 미리 생성하면 불필요한 메모리를 차지할 수 있고, Pool의 크기와 오브젝트 반환 상태를 별도로 관리해야 한다는 점입니다.

우리들의 게임 발매 이야기

안녕하세요. 플밍 4기 입니다. 게임 개발을 배우기 전 네트워크 엔지니어 도메인에서 익히고 배웠던 네트워크 이론에 대한 기초 입니다. 학습에 도움이 되길 바라며 공유 드립니다.
XR을 활용한 게임 개발 3기(유니티) 수강생입니다. 곧 수료 하지만 앞으로 이곳에 가끔 저의 개발 경험이 나 지식 기록할까 합니다. 더 나아가 이 사이트가 제 개인위키의 역할을 할 수 있으면 좋겠습니다. 한국 게임 시장을 흔들겠습니다


안녕하세요. 플밍 4기 입니다. 게임 개발을 배우기 전 네트워크 엔지니어 도메인에서 익히고 배웠던 네트워크 이론에 대한 기초 입니다. 학습에 도움이 되길 바라며 공유 드립니다.
XR을 활용한 게임 개발 3기(유니티) 수강생입니다. 곧 수료 하지만 앞으로 이곳에 가끔 저의 개발 경험이 나 지식 기록할까 합니다. 더 나아가 이 사이트가 제 개인위키의 역할을 할 수 있으면 좋겠습니다. 한국 게임 시장을 흔들겠습니다
