프로파일링 (2) - 모바일 프로파일링 - soo:bak
작성일 :
빌드 프로파일링과 플랫폼 도구
Part 1에서는 Unity Profiler와 Frame Debugger로 병목을 읽는 기본 흐름을 다뤘습니다. Unity Profiler는 CPU 호출 구조, Timeline의 스레드 흐름, GPU 시간, Rendering 수량 지표, Memory 사용량을 보여주고, Frame Debugger는 한 프레임의 드로우콜과 렌더링 패스 순서를 확인하게 해줍니다.
다만 성능 문제는 Unity 엔진 내부에서만 발생하지 않습니다. 에디터에서 문제없던 장면이 빌드에서는 다르게 동작할 수 있고, 운영 체제의 스레드 스케줄링, 그래픽스 API 호출 흐름, GPU 하드웨어 카운터, 기기의 발열 상태처럼 Unity Profiler만으로는 충분히 보이지 않는 정보도 있습니다.
이때 Unity Profiler와 플랫폼 도구의 역할은 서로 다릅니다. Unity Profiler는 Unity 안에서 어떤 시스템이 시간을 쓰는지 보여주고, 플랫폼 도구는 그 작업이 운영 체제와 하드웨어 위에서 어떻게 실행되었는지를 보여줍니다.
예를 들어 Unity Profiler에서 CPU 시간이 길게 보인다면, 시스템 트레이스 도구로 해당 스레드가 어떤 코어에서 실행되었는지, I/O 대기나 다른 프로세스의 영향이 있었는지 확인할 수 있습니다. GPU 시간이 높게 보인다면, GPU 벤더 도구로 셰이더 실행 비용, 메모리 대역폭, 타일 기반 렌더링 효율 같은 하드웨어 지표를 더 자세히 볼 수 있습니다.
이 글에서는 빌드에서 얻은 프로파일링 데이터를 Android/iOS의 시스템·GPU 도구와 함께 해석하는 방법을 살펴봅니다. 또한 한 번의 측정으로 끝내지 않고, 동일한 조건에서 반복 측정하고 최적화 전후의 변화를 비교하는 프로파일링 워크플로우를 다룹니다.
Android 시스템 레벨 프로파일링
Unity Profiler는 Unity 프로세스 안에서 어떤 코드와 엔진 시스템이 시간을 쓰는지 보여줍니다. 하지만 Android 기기에서 실제 프레임 시간은 운영 체제의 스레드 스케줄링, CPU 코어 배치, I/O 대기, 다른 프로세스의 영향도 함께 받습니다.
Android 시스템 레벨 프로파일링은 이 부분을 확인하기 위한 단계입니다. Unity 내부의 함수 호출을 보는 것이 아니라, 게임 스레드가 운영 체제 위에서 언제 실행되고 언제 대기했는지를 시간축으로 추적합니다. GPU 내부 동작을 보는 도구는 뒤에서 별도로 다루고, 여기서는 먼저 CPU 스케줄링과 시스템 이벤트를 확인하는 흐름을 살펴봅니다.
systrace와 Perfetto
Android의 시스템 트레이스는 운영 체제 수준의 실행 흐름을 기록하는 기능입니다. 과거에는 systrace라는 이름으로 많이 사용되었고, 최근 Android 프로파일링에서는 Perfetto 기반의 트레이스 분석이 널리 사용됩니다. Android 버전과 수집 방식에 따라 저장 형식이나 표시 항목은 달라질 수 있지만, 핵심은 동일합니다. Unity 프로세스 밖에서 CPU 스케줄링, 프레임 제출, I/O 대기 같은 시스템 이벤트를 시간축으로 확인하는 것입니다.
Unity Profiler가 Unity 엔진 내부의 함수 호출과 모듈별 시간을 보여준다면, 시스템 트레이스는 게임 프로세스가 운영 체제 위에서 언제 실행되고 언제 대기했는지를 보여줍니다. 수집 설정과 기기 환경에 따라 다르지만, 대표적으로 다음 정보를 함께 볼 수 있습니다.
| 카테고리 | 확인할 수 있는 것 |
|---|---|
| CPU 스케줄링 | 게임 스레드가 어느 CPU에서 실행되었는지, 실행되지 못하고 대기한 구간이 있는지 |
| CPU 상태 | 코어별 주파수, 유휴 상태, 고성능·효율 코어 사용 양상 |
| 프레임 제출 | 프레임이 언제 제출되고 화면 합성 단계에서 지연이 있었는지 |
| I/O 활동 | 파일 읽기·쓰기 시점과 지속 시간, 에셋 로딩으로 인한 대기 가능성 |
| 메모리 관련 이벤트 | 프로세스 메모리 변화, 메모리 압력, 시스템 차원의 메모리 이벤트 |
시스템 트레이스가 유용한 대표적인 경우는 Unity Profiler에서 특정 함수가 두드러지게 비싸 보이지 않는데도 빌드에서 프레임 드롭이 발생하는 상황입니다. 이때 문제는 Unity 내부 함수의 실행 시간보다, 스레드가 운영 체제 위에서 언제 실행 기회를 받았는지와 관련될 수 있습니다.
시스템 트레이스는 Unity Profiler의 Timeline 뷰와 비슷하게 시간축을 기준으로 읽습니다. 차이는 범위입니다. Unity Profiler의 Timeline이 Unity 프로세스 내부의 작업 흐름을 보여준다면, 시스템 트레이스는 Unity 프로세스 바깥의 다른 프로세스, CPU 스케줄링, I/O 대기, 프레임 제출 흐름까지 함께 보여줍니다.
따라서 Unity 메인 스레드나 렌더 스레드가 실제로 실행된 구간, 대기한 구간, 다른 프로세스와 CPU를 나눠 쓴 구간을 함께 볼 수 있습니다. 코어 번호만으로 성능을 단정하기보다는, 스레드 실행 구간과 코어별 주파수, 프레임 마감 시점을 함께 보며 해석하는 편이 적절합니다.
Android GPU 프로파일링 — ARM Mali
Android 기기 중에는 Arm Mali 계열 GPU를 사용하는 기기가 많습니다. Mali GPU에서 GPU 병목을 더 자세히 확인하려면 Unity Profiler의 GPU 모듈만으로는 부족할 수 있습니다. Unity Profiler가 렌더링 단계별 시간을 보여주는 데 초점이 있다면, Arm의 도구는 GPU 하드웨어 카운터를 통해 셰이더 실행, 메모리 대역폭, 타일 처리 같은 내부 지표를 확인하는 데 사용합니다.
Arm GPU 분석에는 Arm Performance Studio를 사용할 수 있습니다. 그중 Streamline은 CPU/GPU 카운터를 시간축으로 수집해, 어느 구간에서 부하가 커졌는지 확인할 때 사용합니다.
Streamline이 제공하는 데이터
Streamline은 CPU와 GPU의 하드웨어 카운터를 시간축으로 수집합니다. 카운터 이름과 제공 범위는 GPU 세대, 드라이버, 수집 템플릿에 따라 달라질 수 있으므로, 특정 카운터 하나를 외우기보다 어떤 유형의 병목을 구분할 수 있는지 이해하는 편이 적절합니다.
Mali GPU 카운터 예시 — Streamline
| 카운터 그룹 | 확인할 수 있는 것 | 해석 방향 |
|---|---|---|
| 셰이더 작업량 | Vertex/Fragment 단계의 사이클, 실행 비중, 사용률 | 프래그먼트 쪽 비중이 크면 오버드로우, 복잡한 픽셀 셰이더, 비싼 텍스처 샘플링을 우선 확인 |
| 타일·프레임버퍼 처리 | 타일 처리량, 로드/스토어, Transaction Elimination 관련 지표 | 렌더 타깃을 자주 읽고 쓰거나, 프레임버퍼 변경 영역이 넓은지 확인 |
| 메모리 대역폭 | 외부 메모리 읽기·쓰기, 텍스처 접근, 버스 사용량 | 텍스처 해상도, 포맷, 렌더 타깃 크기, 불필요한 메모리 왕복 여부를 확인 |
| 지오메트리·타일링 | 버텍스 처리량, primitive/tile 관련 작업량 | 정점 수, LOD, 컬링, 드로우콜 구성 문제를 확인 |
GPU 아키텍처 (2) - 모바일 GPU와 TBDR에서 다룬 것처럼, 타일 기반 GPU에서는 보이지 않는 프래그먼트를 얼마나 일찍 줄이느냐가 중요합니다. Unity Profiler에서 Transparent나 GPU 시간이 높게 보였다면, Streamline에서는 프래그먼트 작업량, 메모리 대역폭, 타일 로드/스토어 같은 카운터를 함께 확인하여 비용이 픽셀 처리에서 발생하는지, 메모리 이동에서 발생하는지 구분합니다.
예를 들어 프래그먼트 관련 카운터가 높고 메모리 대역폭도 함께 높다면, 오버드로우나 큰 렌더 타깃, 무거운 픽셀 셰이더가 원인 후보가 됩니다. 반대로 지오메트리·타일링 관련 지표가 높다면 정점 수, LOD, 컬링 상태를 먼저 확인하는 편이 적절합니다. Streamline은 Unity Profiler에서 “GPU가 느리다”까지 좁힌 뒤, GPU 내부의 어느 자원이 바쁜지 확인하는 단계로 사용합니다.
프레임 단위 분석
Streamline은 시간축으로 성능 변화를 보는 데 적합하지만, 특정 프레임이 왜 무거운지까지 바로 설명해주지는 않습니다. 어느 시점의 GPU 카운터가 높게 나타났다면, 그 프레임을 따로 캡처해 렌더링 패스와 드로우콜 단위로 살펴보는 단계가 필요합니다.
Arm Performance Studio에서는 이런 용도로 Frame Advisor와 RenderDoc for Arm GPUs를 사용할 수 있습니다. Unity의 Frame Debugger가 엔진 수준에서 드로우콜 순서와 렌더링 상태를 보여준다면, 이런 프레임 분석 도구는 실제 그래픽스 API 호출, 렌더 패스 구성, 드로우콜별 지오메트리와 셰이더 비용을 더 자세히 확인하는 데 사용합니다.
예를 들어 Streamline에서 프래그먼트 작업량이 높은 구간을 찾았다면, 해당 프레임을 캡처해 어떤 패스와 드로우콜이 화면을 많이 덮는지 확인합니다. 특정 셰이더나 렌더 타깃이 비용을 만들고 있다면, 그 지점에서 텍스처 샘플링, 셰이더 복잡도, 렌더 타깃 크기, 불필요한 패스 실행 여부를 좁혀볼 수 있습니다.
Android GPU 프로파일링 — Qualcomm Adreno
Android 기기 중에는 Qualcomm Snapdragon SoC와 Adreno GPU를 사용하는 기기도 많습니다. Adreno GPU에서 GPU 병목을 더 자세히 확인할 때는 Qualcomm이 제공하는 Snapdragon Profiler를 사용할 수 있습니다.
Snapdragon Profiler는 Adreno GPU만 따로 보는 도구라기보다, Snapdragon 기반 기기의 CPU, GPU, 메모리, 전력, 열 상태를 함께 보는 성능 분석 도구입니다. Unity Profiler에서 CPU 또는 GPU 병목 후보를 찾은 뒤, Snapdragon Profiler로 하드웨어 카운터와 시스템 상태를 함께 확인하는 흐름으로 사용합니다.
Snapdragon Profiler로 확인할 수 있는 것
Snapdragon Profiler에서 확인하는 데이터는 크게 세 범위로 나눌 수 있습니다. 플레이 중 계속 변하는 실시간 지표, 일정 구간을 기록한 트레이스, 특정 프레임의 그래픽스 상태입니다. 기기, 드라이버, 그래픽스 API, 도구 버전에 따라 표시되는 항목은 달라질 수 있으므로, 고정된 카운터 이름보다 어떤 문제를 확인하려는지에 맞춰 보는 편이 적절합니다.
Snapdragon Profiler의 활용 범위
| 분석 범위 | 확인할 수 있는 것 | 활용 |
|---|---|---|
| Realtime metrics | CPU, GPU, 메모리, 전력, 열 상태, 하드웨어 카운터의 시간 변화 | 플레이 중 특정 구간에서 부하가 언제 올라가는지 확인 |
| Trace capture | 일정 시간 동안의 CPU/GPU 카운터와 실행 흐름 | GPU busy, 메모리 대역폭, 주파수 변화, 열 상태 변화처럼 프레임 구간의 병목 후보를 추적 |
| Snapshot capture | 특정 프레임의 그래픽스 워크로드, 리소스, 렌더 상태 | 드로우콜, 셰이더, 텍스처, 렌더 타깃, 파이프라인 상태를 프레임 단위로 조사 |
Unity Profiler에서 GPU 시간이 높게 나타났다면, Snapdragon Profiler에서는 그 시간이 어떤 자원 때문에 늘었는지를 봅니다. 셰이더 연산이 많은지, 텍스처 접근이나 메모리 대역폭이 높은지, GPU 주파수가 낮아졌는지, 열 상태 때문에 성능이 제한되고 있는지 같은 항목을 함께 확인합니다.
오버드로우도 같은 방식으로 해석합니다. Unity의 Overdraw 뷰나 Frame Debugger로 겹쳐 그려지는 영역을 찾고, Snapdragon Profiler에서는 그 구간에서 프래그먼트 작업량, 메모리 대역폭, GPU busy 같은 지표가 함께 높아지는지 확인합니다. 화면상으로 많이 겹쳐 보이는 것과 실제 GPU 병목은 항상 같지 않으므로, 시각화 결과와 하드웨어 카운터를 함께 보는 편이 안전합니다.
Adreno GPU의 LRZ(Low Resolution Z-buffer)는 깊이 정보를 낮은 해상도로 요약해, 뒤에 가려질 가능성이 큰 프래그먼트를 더 이른 단계에서 걸러내는 기법입니다. GPU 아키텍처 (2) - 모바일 GPU와 TBDR에서 다룬 것처럼, 이런 깊이 기반 제거는 주로 불투명 오브젝트에서 의미가 있습니다.
핵심은 GPU가 프래그먼트 셰이더를 실행하기 전에 “이 픽셀은 앞의 물체에 가려진다”고 판단할 수 있어야 한다는 점입니다. 일반적인 불투명 렌더링처럼 depth test/write가 안정적으로 동작하는 경우에는 이런 판단이 비교적 쉽습니다. 반대로 셰이더가 depth 값을 직접 바꾸거나, discard로 픽셀을 버릴지 말지를 셰이더 안에서 결정하거나, 반투명 블렌딩처럼 뒤의 색과 섞어야 하는 경우에는 조기 제거의 이점이 줄어들 수 있습니다.
Snapdragon Profiler에서 LRZ와 관련된 지표가 어떤 이름으로 표시되는지는 기기, 드라이버, 그래픽스 API, 도구 버전에 따라 달라질 수 있습니다. 따라서 특정 카운터 하나만 찾기보다, 불투명 오브젝트의 렌더링 순서나 머티리얼 상태를 조정했을 때 GPU 시간, 픽셀/프래그먼트 관련 지표, 메모리 대역폭이 함께 줄어드는지를 비교하는 편이 적절합니다.
반투명 오브젝트나 UI처럼 깊이 기반 제거의 도움을 받기 어려운 대상은 LRZ보다 겹침 면적, 레이어 구성, 렌더링 순서를 먼저 확인합니다.
iOS 프로파일링
iOS에서는 Android의 SoC 벤더 도구 대신, Xcode에 포함된 Apple의 프로파일링 도구를 사용합니다. Unity 프로젝트가 iOS 빌드에서 Metal을 통해 렌더링되므로, 프레임 단위의 GPU 분석은 Xcode의 Metal 관련 도구로 확인하고, CPU·메모리·전력 같은 시스템 수준의 정보는 Instruments에서 함께 봅니다.
Xcode GPU Debugger
Xcode의 Metal Debugger는 실행 중인 앱의 Metal 워크로드를 캡처해, 특정 프레임이 어떤 렌더 패스와 드로우콜로 구성되었는지 확인하는 도구입니다. Unity의 Frame Debugger가 엔진이 제출한 렌더링 흐름을 보여준다면, Metal Debugger는 iOS 빌드에서 실제 Metal 명령, 파이프라인 상태, 리소스 바인딩을 프레임 단위로 살펴보는 데 사용합니다.
Metal Debugger에서 확인할 수 있는 것
| 확인 범위 | 확인할 수 있는 것 | 활용 |
|---|---|---|
| 프레임 구조 | 렌더 패스, 드로우콜, 컴퓨트 디스패치의 순서 | 한 프레임이 어떤 단계로 구성되는지 확인 |
| 파이프라인 상태 | 셰이더, 렌더 타깃, 블렌딩·깊이 상태, 리소스 바인딩 | 특정 드로우콜이 어떤 상태로 실행되었는지 조사 |
| 리소스 내용 | 텍스처, 버퍼, 렌더 타깃의 내용과 사용 위치 | 잘못된 리소스 바인딩이나 불필요한 중간 렌더 타깃 확인 |
| 성능 정보 | GPU trace와 리플레이 결과에서 제공되는 단계별 비용 정보 | 무거운 렌더 패스나 셰이더 후보를 좁힘 |
이 표의 항목은 GPU 시간이 높은 프레임에서 원인을 좁힐 때 특히 유용합니다. GPU 아키텍처 (2) - 모바일 GPU와 TBDR에서 다룬 것처럼, Apple GPU는 화면을 작은 타일로 나누어 처리합니다. 타일 안에서 앞쪽 불투명 표면이 깊이값을 기록하면, 뒤에 가려지는 표면은 프래그먼트 셰이더를 실행하기 전에 제외될 수 있습니다.
따라서 Metal Debugger에서는 먼저 비용이 큰 렌더 패스와 드로우콜을 찾고, 그 드로우콜의 상태를 확인합니다. depth test/write가 켜져 있는지, 블렌딩이 필요한지, 어떤 렌더 타깃에 그리고 있는지, 프래그먼트 셰이더 비용이 높은지를 함께 보면 픽셀 비용이 어디에서 쌓이는지 좁힐 수 있습니다.
예를 들어 불투명 패스라면 깊이 처리나 셰이더 복잡도를 먼저 의심할 수 있고, 반투명 오브젝트·파티클·UI가 많은 패스라면 오버드로우와 블렌딩 비용을 먼저 봅니다. 이 경우 Metal Debugger에서는 무거운 패스와 드로우콜을 찾고, Unity의 Overdraw 뷰나 Frame Debugger에서는 그 드로우콜에 대응하는 오브젝트와 겹침 영역을 확인합니다.
Instruments
Instruments는 iOS 빌드가 실제 기기에서 어떻게 실행되는지 시간축으로 기록하는 프로파일링 도구입니다. Unity Profiler가 Unity 엔진 내부의 모듈별 시간을 보여준다면, Instruments는 CPU 스레드, 시스템 호출, Metal 실행 흐름, 메모리 할당, 전력 사용처럼 운영 체제와 하드웨어에 가까운 정보를 함께 보여줍니다.
Instruments는 여러 기록 도구를 묶어 제공하므로, 측정하려는 대상에 맞춰 필요한 항목을 선택해 사용합니다. 예를 들어 스크립트나 네이티브 함수의 호출 시간은 Time Profiler로 확인할 수 있고, Metal 렌더링에서 CPU가 명령을 제출한 시점과 GPU가 실행한 시점은 Metal System Trace나 Game Performance 템플릿으로 함께 볼 수 있습니다.
Instruments에서 자주 사용하는 항목
| 항목 | 주로 보는 정보 | 활용 |
|---|---|---|
| Time Profiler | CPU에서 실행된 함수와 콜 스택의 시간 비중 | Unity Profiler에서 비싸게 보인 구간을 네이티브 호출 흐름까지 더 좁혀 봄 |
| Metal System Trace | CPU의 Metal 명령 제출, GPU 실행 구간, 동기화 대기 | CPU가 GPU를 기다리는지, GPU가 명령을 기다리는지 확인 |
| Allocations | 객체와 메모리 할당의 시간 변화, 할당 위치 | 특정 플레이 구간에서 메모리가 계속 증가하는 원인 조사 |
| Energy / Power 관련 항목 | CPU·GPU 활동, 디스플레이, 네트워크 등 전력에 영향을 주는 구간 | 프레임 시간은 안정적이지만 발열이나 배터리 소모가 큰 구간 확인 |
Metal System Trace는 CPU가 Metal 명령을 제출하는 시점과 GPU가 그 명령을 실행하는 시점을 같은 시간축에서 보여줍니다. 이 뷰를 보면 CPU가 GPU 작업 완료를 기다리는 구간이나, 반대로 GPU가 새 명령을 기다리는 구간을 확인할 수 있습니다.
대기 구간이 길게 나타난다고 해서 곧바로 특정 함수 하나가 원인이라고 단정할 수는 없습니다. VSync, 프레임 pacing, 리소스 동기화, 커맨드 버퍼 제출 타이밍도 비슷한 패턴을 만들 수 있습니다. 따라서 Metal System Trace에서 대기 구간을 찾은 뒤, Unity Profiler의 Timeline 뷰에서 같은 프레임을 열어 어떤 C# 호출, 렌더링 준비 작업, 리소스 업로드가 겹쳐 있었는지 함께 확인합니다.
실기기 프로파일링 준비
플랫폼 도구를 사용하려면 에디터가 아니라 실제 기기에서 실행 중인 빌드를 기준으로 데이터를 수집해야 합니다. Unity Profiler, Arm Streamline, Snapdragon Profiler, Xcode Instruments 모두 방식은 다르지만, 공통적으로 개발 PC와 기기를 연결하고, 프로파일링 가능한 빌드나 앱 실행 상태를 준비해야 합니다.
Development Build 설정
Unity Profiler를 빌드에 연결하려면 일반적으로 Development Build로 빌드합니다. Development Build에는 Profiler 연결과 진단을 위한 정보가 포함되며, 이 옵션을 켜면 Autoconnect Profiler, Script Debugging, Deep Profiling Support 같은 추가 옵션을 선택할 수 있습니다.
성능 측정용 Build Settings 예시
| 옵션 | 권장 상태 | 이유 |
|---|---|---|
| Development Build | ON | 빌드에서 Unity Profiler 데이터를 수집하기 위한 기본 설정 |
| Autoconnect Profiler | 상황에 따라 ON | 앱 시작 시 Profiler에 자동 연결. 시작 직후 데이터를 놓치지 않아야 할 때 유용 |
| Script Debugging | 보통 OFF | C# 디버거 연결용 옵션. 성능 측정에는 불필요한 디버깅 비용이 섞일 수 있음 |
| Deep Profiling Support | 보통 OFF | 함수 호출을 더 자세히 계측하지만 실행 비용이 크게 늘 수 있음. 필요한 구간에서만 한시적으로 사용 |
Development Build는 릴리스 빌드와 완전히 같은 성능 조건은 아닙니다. Profiler 연결과 개발용 정보가 포함되기 때문입니다. 따라서 Development Build는 병목 위치를 찾는 용도로 사용하고, 최종 성능 판단은 Script Debugging과 Deep Profiling을 끈 상태의 빌드, 필요하다면 릴리스 빌드에서 FPS, 프레임 시간, 발열, 메모리 사용량을 별도로 확인하는 편이 안전합니다.
USB 연결
실기기 프로파일링에서는 USB 연결이 가장 안정적인 편입니다. 기기가 개발 PC에 직접 연결되어 있으므로 Unity Profiler나 플랫폼 도구가 실행 중인 앱을 감지하고 데이터를 수집하기 쉽습니다. 프레임 스파이크나 시작 직후 로딩 구간처럼 짧은 구간을 측정할 때도 연결 상태를 유지하기 좋습니다.
기본 흐름은 기기를 USB로 연결하고, 프로파일링할 빌드를 설치한 뒤, 도구에서 실행 중인 대상을 선택하는 방식입니다. Unity Profiler에서는 실행 중인 Player를 선택하고, Xcode나 벤더 도구에서는 기기에서 실행 중인 앱 세션을 대상으로 캡처나 기록을 시작합니다. 다만 Android와 iOS는 연결에 필요한 준비가 조금 다릅니다.
Android에서는 USB 디버깅을 활성화해야 하고, Unity Profiler 연결이 자동으로 잡히지 않으면 ADB 연결 상태와 포트 포워딩을 확인합니다. Unity가 필요한 처리를 자동으로 해주는 경우가 많지만, 환경에 따라 adb forward를 수동으로 설정해야 할 수도 있습니다.
iOS에서는 Xcode를 통해 빌드를 기기에 설치하고 실행합니다. Unity Profiler에서는 실행 중인 Player를 선택하고, Metal Debugger나 Instruments를 사용할 때는 Xcode에서 같은 기기와 앱 실행 세션을 대상으로 캡처를 시작합니다.
Wi-Fi 연결
Unity Profiler는 USB 없이 네트워크를 통해 빌드에 연결할 수도 있습니다. 이 경우 개발 PC와 기기가 같은 네트워크에 있어야 하며, 연결 대상 목록에서 자동으로 감지된 Player를 선택하거나 기기의 IP 주소를 직접 입력합니다.
Wi-Fi 연결은 케이블 없이 테스트할 수 있어 편리하지만, 연결 상태가 USB보다 불안정할 수 있습니다. 특히 Deep Profiling처럼 전송되는 데이터가 많거나, 앱 시작 직후부터 데이터를 놓치지 않아야 하는 경우에는 USB 연결이 더 안정적입니다. 플랫폼별 GPU 도구나 시스템 트레이스 도구는 각 도구가 요구하는 연결 방식이 다를 수 있으므로, 해당 도구의 연결 절차를 따릅니다.
에디터 프로파일링과 실기기 프로파일링의 차이
에디터 프로파일링은 빠르게 원인을 좁히는 데 유용하지만, 최종 성능 판단을 그대로 대신하기는 어렵습니다. 에디터 자체의 작업이 섞이고, 실행 환경도 실제 기기와 다르기 때문입니다.
에디터와 실기기 빌드의 차이
| 항목 | 에디터에서 측정할 때 | 실기기 빌드에서 측정할 때 |
|---|---|---|
| 실행 환경 | 에디터 UI, Scene View, Inspector 갱신이 함께 실행됨 | 실제 Player가 대상 OS와 하드웨어 위에서 실행됨 |
| CPU/GPU 성능 | 개발 PC의 CPU/GPU 성능에 영향을 받음 | 대상 기기의 CPU/GPU, 전력·열 정책, 드라이버 영향을 받음 |
| 스크립팅 백엔드 | 에디터 런타임 기준으로 실행됨 | 빌드 설정에 따라 IL2CPP/Mono 등 실제 Player 설정이 적용됨 |
| 에셋 포맷 | 에디터용 임포트 상태와 캐시의 영향을 받을 수 있음 | 플랫폼별 압축 포맷, 해상도, 스트리밍 설정이 적용됨 |
| 메모리 상태 | 에디터와 프로젝트 데이터가 함께 메모리를 사용함 | Player, OS, 드라이버, 백그라운드 작업의 영향을 받음 |
| 측정 목적 | 빠른 원인 추정과 반복 확인 | 최종 성능 판단과 플랫폼별 병목 확인 |
에디터 프로파일링은 반복 확인이 빠르기 때문에, 매 프레임 할당이나 특정 함수의 과도한 비용처럼 명확한 문제를 초기에 찾는 데 유용합니다. 다만 목표 플랫폼의 프레임 시간, 발열, 메모리 사용량까지 판단하려면 실기기 빌드에서 다시 확인해야 합니다. 이때 같은 장면을 같은 조건으로 반복 측정할 수 있어야, 최적화 전후의 차이를 신뢰할 수 있습니다.
성능 테스트 방법론
프로파일링에서 중요한 것은 도구 자체보다 같은 조건에서 반복 측정할 수 있는가입니다. 장면, 입력, 카메라 위치, 등장 오브젝트 수, 측정 시간 같은 조건이 매번 달라지면 최적화 전후의 수치를 비교하기 어렵고, 간헐적으로 발생하는 문제도 재현하기 어렵습니다.
최악 조건에서의 테스트
성능 테스트는 평균적인 장면만으로는 부족합니다. 실제 플레이에서 가장 무거워질 수 있는 구간을 따로 정해, 그 상황에서도 목표 프레임 시간을 유지하는지 확인해야 합니다. 예를 들어 많은 캐릭터가 동시에 움직이는 장면, 이펙트와 UI가 한꺼번에 표시되는 장면, 넓은 시야에 오브젝트가 많이 들어오는 장면이 후보가 될 수 있습니다.
중요한 것은 이 조건을 매번 같은 방식으로 재현할 수 있어야 한다는 점입니다. 카메라 위치, 등장 오브젝트 수, 이펙트 발생 타이밍, 입력 시퀀스를 고정한 테스트 씬이나 자동화된 플레이 루틴을 만들어 두면, 최적화 전후의 변화를 같은 기준으로 비교할 수 있습니다.
기준 기기에서의 테스트
성능 목표는 먼저 기준 기기를 정한 뒤 판단하는 편이 적절합니다. 개발 PC나 고성능 기기에서 문제가 없더라도, 실제 대상 플랫폼의 하드웨어 성능, 메모리 여유, 전력·열 정책은 다를 수 있습니다.
기준 기기는 프로젝트가 실제로 지원하려는 최소 사양이나 대표 사양에 가까워야 합니다. 최악 조건 테스트도 이 기기에서 통과해야 실제 지원 범위 안에서 성능 목표를 만족한다고 볼 수 있습니다. 고사양 기기에서의 그래픽 품질 향상은 그 이후에 다룰 문제입니다.
서멀 쓰로틀링 테스트
모바일 기기는 SoC의 열 한계에 가까워지면 서멀 쓰로틀링(Thermal Throttling)으로 CPU와 GPU의 최대 클럭을 단계적으로 낮춥니다. 게임 시작 직후에는 60fps를 유지하더라도, 몇 분 동안 부하가 누적되면 같은 장면에서도 프레임 시간이 길어지고 40fps나 30fps 수준으로 떨어질 수 있습니다. 따라서 모바일 성능은 기기가 차가울 때의 순간적인 피크가 아니라, 열이 누적된 뒤에도 유지되는 지속 성능으로 평가해야 합니다.
서멀 쓰로틀링 테스트의 목적은 같은 작업량을 유지한 상태에서 시간이 지날수록 성능이 떨어지는지 확인하는 것입니다. 기준 기기에서 재현 가능한 플레이 시나리오를 연속 실행하고, 실행 초반의 성능과 기기가 예열된 뒤의 성능을 나누어 기록합니다. 측정 시간은 프로젝트의 일반적인 플레이 세션 길이에 맞추되, 열 제한이 드러날 만큼 충분히 길게 잡아야 합니다.
비교가 의미 있으려면 각 테스트의 시작 조건도 맞춰야 합니다. 이전 실행의 열이 남아 있으면 같은 빌드라도 쓰로틀링이 더 일찍 시작될 수 있으므로, 기기가 비슷한 초기 온도로 돌아온 뒤 다시 측정합니다. 화면 밝기, 충전 상태, 케이스 유무, 주변 온도처럼 발열에 영향을 주는 조건도 가능한 한 동일하게 유지합니다.
측정할 때는 평균 프레임 시간과 P99뿐 아니라 CPU/GPU 클럭, 사용률, 온도 또는 플랫폼 도구의 열 상태를 함께 봅니다. 같은 시나리오를 실행하는데 시간이 지날수록 프레임 시간이 길어지고 CPU나 GPU 클럭이 내려간다면 서멀 쓰로틀링을 의심할 수 있습니다. 이때 온도 상승이나 열 경고가 함께 나타나면, 장면이 더 무거워진 것이 아니라 낮아진 클럭 때문에 같은 작업을 처리하는 시간이 늘어난 것으로 해석할 수 있습니다.
예열된 상태에서 프레임 시간이 목표 예산을 지속적으로 넘는다면, 개별 스파이크보다 매 프레임 반복되는 비용을 먼저 줄여야 합니다. 드로우콜, 셰이더 복잡도, 오버드로우, 물리 연산처럼 CPU/GPU를 계속 바쁘게 만드는 요소를 줄이면 평균 부하와 발열 누적을 낮출 수 있습니다. 그래도 지속 성능이 목표에 미치지 못하면, 목표 프레임률, 렌더 해상도, 그래픽 품질을 낮춰 장시간 유지 가능한 성능 범위에 맞춥니다.
메모리 워터마크 측정
메모리 워터마크(Memory Watermark)는 게임 실행 중 메모리 사용량이 가장 높았던 지점입니다. 평균 사용량이 낮아도 장면 전환, 보스전, 결과 화면처럼 새 에셋을 많이 로드하거나 이전 에셋과 다음 에셋이 잠시 함께 유지되는 구간에서는 피크가 크게 올라갈 수 있습니다. 메모리 관리 (2) - 네이티브 메모리와 에셋에서 다룬 것처럼, 모바일에서는 OS와 다른 앱도 같은 메모리를 사용하므로 이 피크가 중요합니다. 피크가 기기에서 감당할 수 있는 범위에 가까워질수록 여유 메모리가 줄어들고, 심하면 OOM Kill이나 큰 스터터로 이어질 수 있습니다.
메모리 워터마크는 주요 장면을 따로 열어보는 것보다, 실제 플레이 흐름을 따라가며 측정하는 편이 정확합니다. 타이틀, 로비, 게임플레이, 보스전, 결과 화면처럼 사용자가 지나가는 경로를 순서대로 진행하고, Unity Profiler의 Memory 모듈과 플랫폼 메모리 도구에서 앱 메모리의 최고치를 기록합니다. 특히 장면 전환, Addressables나 AssetBundle 로드, 결과 화면 표시처럼 이전 리소스가 해제되기 전에 새 리소스가 올라오는 구간이 피크가 되기 쉽습니다. 최소 사양 기기에서는 이 피크가 프로젝트의 메모리 예산 안에 들어와야 합니다. 피크가 지나간 뒤에도 메모리가 기준선으로 내려오지 않는다면, 해제되어야 할 에셋이나 캐시가 남아 있는지 추가로 확인해야 합니다.
프로파일링 데이터 기록과 비교
앞에서는 최악 조건, 기준 기기, 서멀 쓰로틀링, 메모리 워터마크처럼 성능을 확인해야 하는 주요 상황을 살펴봤습니다. 이 측정값은 한 번 보고 끝내기보다, 같은 형식으로 남겨 두어야 의미가 있습니다. 최적화 전후의 변화를 비교하고, 빌드가 바뀌었을 때 성능 회귀(regression)가 발생했는지 확인하려면 매번 같은 항목을 기록해야 합니다.
프로파일링 데이터 기록 항목
| 기록 항목 | 측정 데이터 |
|---|---|
| 테스트 조건 | 테스트 시나리오, 장면, 카메라 위치, 입력 루틴, 측정 시간, 반복 횟수 |
| 실행 환경 | 기기 모델, OS 버전, Unity 버전, 빌드 설정, 그래픽스 API, 품질 설정 |
| 프레임 시간 | 평균, 중앙값(P50), P95/P99, 최대값, 목표 프레임 예산 초과 횟수 |
| CPU | Main Thread/Render Thread 시간, 스크립트, 물리, 애니메이션, GC Alloc과 GC 발생 시간 |
| GPU·렌더링 | GPU 프레임 시간, 렌더 패스별 비용, 드로우콜 수, SetPass 수, 삼각형 수, 오버드로우 후보 |
| 메모리 | 피크 메모리(워터마크), Unity/Native 메모리, GC Heap, 주요 에셋 카테고리별 사용량 |
| 서멀 상태 | 초반/예열 후 프레임 시간 변화, CPU/GPU 클럭, 기기 온도 또는 열 상태, 충전·밝기 조건 |
평균 프레임 시간만으로는 체감 성능을 판단하기 어렵습니다. 대부분의 프레임이 16ms 안에 끝나더라도, 간헐적으로 50ms 이상 걸리는 프레임이 섞이면 플레이어는 끊김을 느낍니다. 평균은 이런 긴 프레임을 쉽게 숨기므로, P95나 P99(99번째 백분위)처럼 프레임 시간 분포의 끝부분을 함께 봐야 합니다. P99이 50ms라면 전체 프레임 중 느린 1% 구간이 50ms 안팎까지 늘어난다는 뜻이므로, 평균보다 실제 끊김 체감에 더 가깝습니다.
최적화 후에는 같은 기기, 같은 빌드 설정, 같은 시나리오에서 같은 항목을 다시 측정해 이전 기록과 비교합니다. 평균만 좋아졌는지, P99과 최대값도 줄었는지, CPU/GPU 병목 위치가 바뀌었는지, 메모리 워터마크나 서멀 상태가 악화되지 않았는지를 함께 봅니다. 특정 수치 하나만 좋아지고 다른 항목이 나빠졌다면, 그 최적화가 실제 사용자 경험을 개선했는지 다시 판단해야 합니다.
프로파일링 도구 선택 가이드
프로파일링 도구는 문제의 범위를 좁히는 순서에 맞춰 선택하는 편이 효율적입니다. 먼저 Unity Profiler와 Frame Debugger로 병목이 CPU, GPU, 메모리, 렌더링 상태 중 어디에 가까운지 확인하고, 그 다음 필요한 경우 플랫폼 도구로 내려가 운영 체제나 GPU 하드웨어 수준의 원인을 확인합니다.
상황별 프로파일링 도구 선택
| 확인하려는 문제 | 우선 사용할 도구 |
|---|---|
| 특정 프레임에서 CPU 시간이 튀는 원인 | Unity Profiler CPU 모듈의 Hierarchy와 Timeline |
| GC나 동적 할당으로 인한 스파이크 | Unity Profiler의 GC Alloc, Memory Profiler의 스냅샷 비교 |
| 드로우콜 증가, 배칭 실패, 렌더 패스 순서 | Frame Debugger, Unity Profiler Rendering 모듈 |
| 메모리 피크, 에셋 잔류, 씬 전환 후 메모리 증가 | Unity Memory Profiler, 플랫폼 메모리 도구 |
| Unity Profiler에서 GPU 시간이 높게 보이는 경우 | 대상 GPU에 맞는 벤더 도구: Arm Streamline, Snapdragon Profiler, Xcode Metal Debugger |
| GPU 내부 병목이 셰이더, 대역폭, 렌더 패스 중 어디인지 확인 | Arm Streamline/Frame Advisor, Snapdragon Profiler, Xcode Metal Debugger |
| Unity 내부 원인이 뚜렷하지 않은 CPU 지연 | Perfetto(Android), Instruments(iOS) |
| 장시간 플레이 후 성능 하락과 클럭 저하 | 장시간 반복 프로파일링, 벤더 도구의 클럭·온도·열 상태 지표 |
Unity Profiler와 Frame Debugger만으로 원인을 설명하기 어렵다면, 플랫폼 도구나 GPU 벤더 도구를 사용합니다. Unity 안에서 원인이 보이면 그 범위에서 수정하고, 스레드 스케줄링, GPU 하드웨어 카운터, 열 상태처럼 Unity 밖의 정보가 필요할 때만 플랫폼 도구로 확장합니다.
마무리
프로파일링은 단일 도구의 사용법보다, 같은 현상을 여러 관점에서 맞춰 보는 과정에 가깝습니다. Unity 안에서 어떤 시스템이 시간을 쓰는지 확인하고, 필요할 때 운영 체제와 GPU 하드웨어 수준의 지표로 내려가며, 마지막에는 같은 조건에서 다시 측정해 변화가 실제 개선인지 확인해야 합니다.
- 모바일 성능 문제는 엔진 수준, 시스템 수준, GPU 하드웨어 수준의 신호를 함께 봐야 정확히 해석할 수 있습니다.
- 최종 성능 판단은 에디터가 아니라 실제 기기 빌드에서 해야 하며, 기준 기기와 테스트 조건을 고정해야 합니다.
- 평균 프레임 시간만으로는 부족하므로 P95/P99, 최대값, 스파이크 빈도를 함께 봐야 합니다.
- 장시간 플레이에서는 서멀 쓰로틀링과 메모리 워터마크처럼 시간에 따라 변하는 지표도 기록해야 합니다.
- 최적화 전후에는 같은 환경, 같은 시나리오, 같은 항목으로 다시 측정해야 개선 효과를 검증할 수 있습니다.
병목을 측정하지 않으면 최적화는 추측이 됩니다. 반대로 프로파일링으로 병목을 좁히고, 그 지점에 맞는 최적화 기법을 적용하면 개선 효과를 수치로 확인할 수 있습니다.
관련 글
전체 시리즈
- 게임 루프의 원리 (1) - 프레임의 구조
- 게임 루프의 원리 (2) - CPU-bound와 GPU-bound
- 렌더링 기초 (1) - 메쉬의 구조
- 렌더링 기초 (2) - 텍스처와 압축
- 렌더링 기초 (3) - 머티리얼과 셰이더 기초
- GPU 아키텍처 (1) - GPU 병렬 처리와 렌더링 파이프라인
- GPU 아키텍처 (2) - 모바일 GPU와 TBDR
- Unity 렌더 파이프라인 (1) - Built-in과 URP의 구조
- Unity 렌더 파이프라인 (2) - 드로우콜과 배칭
- Unity 렌더 파이프라인 (3) - 컬링과 오클루전
- 스크립트 최적화 (1) - C# 실행과 메모리 할당
- 스크립트 최적화 (2) - Unity API와 실행 비용
- 메모리 관리 (1) - 가비지 컬렉션의 원리
- 메모리 관리 (2) - 네이티브 메모리와 에셋
- 메모리 관리 (3) - Addressables와 에셋 전략
- UI 최적화 (1) - 캔버스와 리빌드 시스템
- UI 최적화 (2) - UI 최적화 전략
- 조명과 그림자 (1) - 실시간 조명과 베이크
- 조명과 그림자 (2) - 그림자와 후처리
- 셰이더 최적화 (1) - 셰이더 성능의 원리
- 셰이더 최적화 (2) - 셰이더 배리언트와 키워드 관리
- 물리 최적화 (1) - 물리 엔진의 실행 구조
- 물리 최적화 (2) - 물리 최적화 전략
- 파티클과 애니메이션 (1) - 파티클 시스템 최적화
- 파티클과 애니메이션 (2) - 애니메이션 최적화
- 프로파일링 (1) - Unity Profiler와 Frame Debugger
- 프로파일링 (2) - 모바일 프로파일링 (현재 글)
- 모바일 전략 (1) - 발열과 배터리
- 모바일 전략 (2) - 빌드와 품질 전략