프로파일링 도구 사용법
Unreal Insights·stat 명령·GPU Visualizer·메모리 도구로 프레임과 자원 병목을 측정합니다.
게임이 느려졌을 때 가장 흔한 실수는 짐작으로 고치는 것입니다. 액터가 많아서 느리다고 생각해 Tick을 줄였는데 실제로는 그림자 계산이 문제였다면, 시간만 쓰고 성능은 그대로입니다. 프레임 속도(FPS), 반응성, 로딩 시간은 모두 플레이어 경험에 직접 영향을 주지만, 무엇이 그 속도를 정하고 있는지는 측정해 보기 전에는 알 수 없습니다.
멀티플레이어 게임 최적화에서 네트워크 비용을 측정하는 도구를 보았다면, 이 글에서는 게임 전체의 성능을 측정하는 프로파일링(profiling) 도구를 살펴봅니다. 언리얼 인사이트(Unreal Insights), 인게임 stat 명령, GPU 비주얼라이저, 메모리와 에셋 도구를 차례로 다루고, 측정한 숫자에서 병목(bottleneck)을 읽어 내는 방법과 최적화를 진행하는 순서를 정리합니다.
프로파일링으로 알아내는 것
프로파일링은 소프트웨어의 성능을 측정하고 분석하는 과정입니다. 게임 개발에서는 주로 세 가지를 알아내는 데 씁니다. 첫째는 병목, 즉 CPU, GPU, 메모리, 입출력 가운데 어떤 자원이 지나치게 쓰여 전체 성능을 끌어내리는 지점입니다. 둘째는 원인으로, 렌더링, 물리, 애니메이션, AI, 네트워크 같은 시스템이나, 폴리곤이 지나치게 많은 메시, 큰 텍스처, 최적화되지 않은 머티리얼 같은 에셋 가운데 무엇이 문제인지입니다. 셋째는 어느 부분을 고쳤을 때 가장 큰 효과를 얻을 수 있는지입니다.
언리얼 엔진은 게임을 실행하는 동안 실시간으로, 또는 세션을 기록해 두었다가 성능 데이터를 모아 보여 주는 여러 내장 도구를 제공합니다.
Unreal Insights: 기록하고 분석하기
언리얼 엔진 5에서는 언리얼 인사이트가 프로파일링의 중심 도구입니다. 언리얼 인사이트는 CPU 작업, GPU 작업, 메모리 할당, 로그, 네트워킹 같은 엔진의 여러 이벤트를 실시간으로 받아 보거나 파일로 기록해 자세히 분석하는 독립 실행형 애플리케이션입니다.
데이터 기록하기
기록은 실행 파일에 -trace= 옵션으로 채널을 지정해 시작합니다. 흔히 쓰는 형태는 [YourGame.exe] -trace=cpu,gpu,log,frame입니다. -trace=default는 기본 채널 묶음을 뜻하며 모든 채널이 켜지는 것은 아닙니다. CPU와 프레임만 보고 싶다면 -trace=cpu,frame처럼 필요한 채널만 고르고, 더 자세한 이름이 필요하면 -statnamedevents도 검토합니다.
게임 안의 콘솔에서도 기록할 수 있습니다. Trace.File Capture.utrace cpu,gpu,log,frame으로 파일 기록을 시작하고, Trace.Stop으로 멈추고, Trace.Status로 현재 상태를 확인합니다. 이렇게 상대 파일 이름을 주면 프로젝트의 Saved/Profiling에 기록되며, 트레이스 스토어(Trace Store)로 보낸 세션은 스토어의 저장 위치에 있습니다.
Unreal Insights 열기
언리얼 엔진 설치 폴더의 Engine\Binaries\Win64\UnrealInsights.exe를 실행하면, 지금 연결된 세션(Live Session)을 고르거나 이전에 저장한 .utrace 파일을 열 수 있습니다. 에디터에서는 아래쪽 도구 모음의 Trace 메뉴에서 Unreal Insights의 세션 브라우저를 열 수도 있습니다. 세션 브라우저에는 트레이스 스토어에 저장된 세션이 목록으로 나오고, 지금 기록 중인 세션은 상태 칸에 LIVE로 표시되어 분석하는 동안에도 계속 갱신됩니다. 목록의 세션을 더블클릭하거나, 탐색기의 .utrace 파일을 창으로 끌어다 놓으면 분석 화면이 열립니다.
타이밍 뷰와 다른 분석 화면
가장 중요한 화면은 트레이스를 분석하는 주 창인 타이밍 인사이트(Timing Insights)의 타이밍 뷰(Timing View)로, 시간에 따른 CPU와 GPU 작업을 타임라인으로 보여 줍니다. 각 트랙의 막대를 눌러 작업의 자세한 정보를 보고, 확대, 축소, 이동으로 특정 프레임이나 구간을 집중해 분석합니다.
타이밍 뷰의 트랙은 스레드마다 나뉩니다. 게임 스레드(GameThread)에서는 게임 로직, 액터 갱신, 물리 같은 대부분의 게임플레이 CPU 작업이 일어납니다. 렌더 스레드(RenderThread)에서는 렌더링 명령을 준비해 GPU로 보내는 CPU 작업이 일어나고, RHI 스레드(RHIThread)에서는 DirectX나 Vulkan 같은 그래픽 API를 부르는 저수준 작업이 일어납니다. GPU 트랙은 실제로 GPU에서 렌더링이 수행되는 시간을 보여 줍니다. 프레임마다의 CPU, GPU 시간을 막대그래프로 보여 주므로, CPU가 느린지(CPU 바운드) GPU가 느린지(GPU 바운드)를 쉽게 파악할 수 있습니다.
다른 분석 화면도 있습니다. 메모리 인사이트(Memory Insights)는 할당과 해제 이벤트를 분석하는데, -trace=default,memory처럼 프로세스가 시작될 때부터 메모리 채널을 켜야 합니다. CPU 채널만 기록한 파일에는 할당 내역이 저절로 생기지 않으며, 패키지에서 측정할 때는 공식 안내의 Development 구성 같은 지원 조건을 확인합니다. 로그 인사이트(Log Insights)는 게임 로그를 걸러 검색하고 시간에 따른 로그 발생을 보여 주며, 네트워킹 인사이트는 앞 장에서 본 것처럼 네트워크 트래픽, 복제 데이터, RPC 호출을 분석합니다.
인게임 stat 명령: 빠른 진단
stat 명령은 게임 화면 위에 성능 통계를 실시간으로 띄워 주는 빠르고 편한 도구입니다. 게임을 실행하는 중에 ~ 키로 콘솔을 열고 명령을 입력합니다.
| 명령 | 보여 주는 것 |
|---|---|
stat fps | 현재 FPS |
stat unit | 게임 스레드, 드로 스레드, GPU, RHI 시간 같은 주요 CPU, GPU 시간 |
stat scenerendering | 드로 콜, 메시 수 같은 장면 렌더링의 자세한 통계 |
stat gpu | GPU의 자세한 통계 |
stat rhi | 렌더링 하드웨어 인터페이스(RHI) 통계 |
stat particles | 파티클 시스템 통계 |
stat anim | 애니메이션 시스템 통계 |
stat streaming | 텍스처 스트리밍 통계 |
stat audio | 오디오 시스템 통계 |
stat net | 핑, 패킷 손실, 대역폭 같은 네트워크 통계 |
stat game | 게임 로직 통계 |
stat none | 모든 통계 숨기기 |
지원되는 항목은 엔진 버전마다 조금씩 다르므로, 쓰는 버전의 Stat Commands 목록에서 필요한 그룹을 고릅니다.
stat unit으로 병목 좁히기
이 가운데 가장 먼저 볼 것은 stat unit입니다. Frame은 한 프레임 전체에 걸린 시간이고, Game은 액터 갱신과 물리 같은 게임 로직, Draw는 렌더링 명령을 만드는 렌더 스레드, RHIT는 그래픽 API로 명령을 제출하는 RHI 스레드, GPU는 실제 GPU 렌더링 시간입니다.
Frame은 고칠 대상이 아니라, Game, Draw, RHIT, GPU 가운데 어느 값이 Frame과 함께 움직이는지 보기 위한 기준입니다. Game이 가장 높으면 게임 스레드의 CPU 바운드이므로, 게임 로직, 물리, AI, 애니메이션 갱신을 언리얼 인사이트의 타이밍 뷰에서 확인합니다. Draw나 RHIT가 가장 높으면 렌더 스레드나 RHI 스레드의 CPU 바운드이므로, 드로 콜 수, 렌더링 상태 변경, 명령 제출, 동기화 대기를 봅니다. GPU가 가장 높으면 GPU 바운드이므로, 셰이더 복잡도, 오버드로, 폴리곤 수, 해상도, 그리고 ShadowDepths, BasePass, PostProcess 같은 패스의 비용을 profilegpu로 확인합니다. 메모리, 로딩, 텍스처 스트리밍의 문제는 stat unit의 축으로 보이지 않으므로 stat memory, stat streaming, MemReport로 따로 기록합니다.
이렇게 병목의 축을 먼저 좁힌 뒤, stat scenerendering이나 stat anim 같은 세부 통계로 원인을 파고드는 것이 가장 효율적입니다. 판단의 기준이 되는 프레임 예산은 목표 FPS로 정해지는데, 60FPS라면 한 프레임 예산은 약 16.67ms, 30FPS라면 약 33.33ms입니다. 그리고 같은 맵, 같은 카메라, 같은 입력, 같은 빌드 설정에서 여러 프레임을 보고 판단해야 합니다.
숫자로 따라가 보기
같은 장면에서 Frame 17ms, Game 5ms, Draw 6ms, GPU 16ms를 기록했다고 해 봅시다. GPU가 Frame에 가장 가까우므로 GPU 패스의 비용을 먼저 조사할 이유가 있습니다. 이때 세 값을 더한 27ms를 프레임 시간으로 쓰면 안 됩니다. 각 스레드와 GPU는 병렬로 진행되고 서로를 기다리는 시간도 섞여 있으므로, 타임라인에서 실제로 긴 구간을 확인합니다. 60FPS의 예산은 1000/60 ≈ 16.67ms이므로, 이 측정의 17ms는 목표보다 조금 깁니다.
메모리 프로파일링
메모리 사용량을 분석해 메모리 누수나 지나친 사용량을 진단할 수도 있습니다. MemReport는 등록된 메모리 통계와 객체, 리소스 보고 명령을 실행하며, 결과는 보통 Saved/Profiling/MemReports의 .memreport 파일에 남고 실제 경로는 로그에서 찾습니다. -full을 붙이면 보고 항목이 더 늘어나지만, 모든 할당의 호출 스택까지 기록하는 옵션은 아닙니다. stat memory는 현재 메모리 사용량의 요약을 화면에 띄우고, stat streaming은 텍스처 스트리밍의 메모리 사용량을 보여 줍니다.
GPU 비주얼라이저
GPU 바운드 문제를 진단할 때 쓸모 있는 도구로, 렌더링 파이프라인의 단계마다 GPU가 시간을 얼마나 쓰는지 보여 줍니다. 게임을 실행하는 중에 콘솔에 profilegpu를 입력하면, 지원되는 개발 환경에서 GPU 프로파일을 캡처합니다. 결과가 창으로 뜨는지, 로그나 트레이스로 나오는지는 엔진 버전과 플랫폼에 따라 다릅니다.
결과에서는 그림자 패스(Shadow Pass), 베이스 패스(Base Pass), 후처리(Post Process) 같은 렌더링 패스마다 걸린 시간을 확인하고, 항목을 펼쳐 드로 콜, 셰이더, 텍스처 같은 자세한 정보를 봅니다. 베이스 패스나 그림자 패스가 오래 걸린다면 메시의 폴리곤 수, 머티리얼 복잡도, 광원 수가 원인일 가능성이 큽니다. 후처리가 오래 걸린다면 블룸, 모션 블러, 안티에일리어싱 같은 후처리 효과의 복잡도를 줄이는 것을 검토합니다.
에셋 감사 도구
특정 에셋이 성능 문제의 원인이라면 에셋을 살펴보는 도구를 씁니다. 참조 뷰어(Reference Viewer)는 어떤 에셋이 어디에서 참조되는지 보여 주어 필요 없는 로딩을 찾게 해 줍니다. 크기 맵(Size Map)은 에셋과 그 참조 관계의 크기를 보여 주는데, 선택한 크기 지표와 에셋 상태에 따른 값이므로 실행 중인 프로세스 전체의 메모리 측정을 대신하지 않습니다. 메시 에디터나 텍스처 에디터 같은 에셋 에디터의 디테일 패널에서는 폴리곤 수, 텍스처 해상도, 텍스처 그룹, 메모리 사용량을 확인할 수 있습니다.
최적화를 진행하는 순서
성능 최적화는 보통 다음 순서로 진행합니다. 먼저 목표 FPS나 메모리 사용량처럼 분명한 성능 목표를 정합니다. 그다음 최적화를 시작하기 전의 현재 성능을 기준선(baseline)으로 측정해 기록합니다. stat unit, 언리얼 인사이트, profilegpu로 가장 큰 병목을 찾고(예: stat unit에서 Game 시간이 가장 높음), 그 병목을 더 깊이 파고들어 구체적인 원인을 찾습니다(예: stat anim으로 애니메이션 갱신이 문제임을 확인).
원인을 찾았으면 그 원인에 맞게 고칩니다. 애니메이션을 최적화하거나, 폴리곤 수를 줄이거나, 셰이더 복잡도를 낮추는 식입니다. 고친 뒤에는 다시 측정해 기준선과 비교하고, 원하는 성능을 얻을 때까지 이 과정을 되풀이합니다. 프레임 제한, VSync, 동기화 대기가 측정에 섞일 수 있으므로 큰 숫자 하나만으로 원인을 확정하지 말고, 같은 장면의 타임라인과 변경 전후의 값을 함께 확인합니다.
정리
- 프로파일링은 병목, 원인, 개선 효과가 큰 곳을 찾는 과정이며, 짐작이 아니라 측정으로 고칠 곳을 정합니다.
- 언리얼 인사이트는
-trace=채널이나Trace.File명령으로 기록하고, 타이밍 뷰에서 게임 스레드, 렌더 스레드, RHI 스레드, GPU 트랙을 비교합니다. 메모리 인사이트는 시작부터 메모리 채널을 켜야 합니다. stat unit의 Frame은 기준이고, Game, Draw, RHIT, GPU 가운데 Frame과 함께 움직이는 값이 병목의 축입니다. 여러 값을 더해 프레임 시간으로 쓰지 않습니다.- 60FPS의 프레임 예산은 약 16.67ms, 30FPS는 약 33.33ms이며, 같은 조건에서 여러 프레임을 보고 판단합니다.
- 메모리는
MemReport,stat memory,stat streaming으로, GPU 패스는profilegpu로, 에셋은 참조 뷰어와 크기 맵으로 살펴봅니다. - 목표 설정, 기준선 측정, 병목 식별, 원인 진단, 개선, 재측정을 되풀이하며, VSync와 동기화 대기를 함께 고려합니다.
연습 문제
1. VSync가 켜진 장면
다른 장면에서 Frame 16.67ms, Game 3ms, Draw 4ms, GPU 5ms가 나왔고 VSync가 켜져 있습니다. 가장 큰 작업 값인 GPU 5ms를 성능 문제의 원인으로 확정할 수 있을까요?
해설 보기
확정할 수 없습니다. CPU와 GPU의 작업이 모두 화면 주기보다 빨리 끝나고, 나머지 시간은 VSync 때문에 다음 화면 갱신을 기다리는 표시 대기일 수 있습니다. Frame이 60FPS의 예산과 거의 같다는 점도 이를 뒷받침합니다. GPU가 가장 큰 숫자라는 이유만으로 이 대기를 GPU 계산 문제로 보고 고치면 안 됩니다.
프레임 제한, VSync, 동기화 설정을 기록하고 타임라인에서 실제로 긴 구간이 작업인지 대기인지 확인합니다. 이 숫자들은 계산과 판단을 연습하기 위한 값이며, 엔진에서 실제로 측정한 기록은 아닙니다.
2. 30FPS 목표의 예산
같은 장면을 30FPS 목표로 바꾸면 한 프레임의 예산은 얼마일까요? 앞의 Frame 17ms 장면은 이 목표를 만족할까요?
해설 보기
30FPS의 예산은 1000/30 ≈ 33.33ms입니다. Frame 17ms는 이 예산보다 짧으므로, 그 한 번의 측정만 보면 30FPS 목표는 만족합니다. 다만 같은 조건에서 여러 프레임을 보고, 가끔 튀는 긴 프레임이 없는지도 함께 확인해야 합니다.
3. 메모리 할당이 보이지 않는 트레이스
-trace=cpu,frame으로 기록한 파일을 언리얼 인사이트에서 열었는데, 메모리 인사이트에 할당 내역이 보이지 않습니다. 왜 그럴까요?
해설 보기
메모리 채널을 켜지 않고 기록했기 때문입니다. 메모리 인사이트는 -trace=default,memory처럼 프로세스가 시작될 때부터 메모리 채널을 켜야 할당과 해제 이벤트를 기록하며, CPU와 프레임 채널만 기록한 파일에는 할당 내역이 저절로 생기지 않습니다. 메모리 채널을 켜고 처음부터 다시 기록합니다.
언리얼 인사이트의 기록과 분석 방법은 Epic의 Unreal Insights 문서에서, stat 명령의 목록은 Stat Commands 문서에서 확인할 수 있습니다.
다음 글 메모리 관리와 최적화 기법에서는 언리얼의 메모리 관리 방식과 가비지 컬렉션, 메모리 사용량을 줄이는 기법을 살펴봅니다.