전체 글 목록으로

게임 최적화를 실기기에서 검증하기: Unity·UE5·Godot 측정용 빌드

에디터에서 프레임 시간이 줄었다고 플레이어의 기기에서도 게임이 빨라지는 것은 아닙니다. 수치를 비교하기 전에 어떤 빌드에서 무엇을 실행한 결과인지부터 맞춰야 합니다.

출시 성능을 판단할 때는 출시할 빌드에 가까운 구성으로 대상 기기에서 실행합니다. 오래 걸리는 처리의 원인을 찾을 때는 진단 기능이 있는 빌드를 쓰고, 수정 후에는 다시 출시 환경에 가까운 조건에서 효과를 확인합니다. 이 글에서는 출시할 구성에 최소한의 측정 코드를 더한 빌드를 측정용 빌드라고 부릅니다. 프로젝트에서 정해 쓰는 이름이지, 세 엔진이 공통으로 제공하는 정식 빌드 구성은 아닙니다.

확인하려는 문제에 맞춰 빌드 선택하기

에디터 프로파일링은 개발 중에 느린 스크립트, 에셋 처리, 메모리 할당을 조사하는 데 유용합니다. Unity나 Unreal Engine의 Development 빌드처럼 진단 기능이 있는 독립 실행형 빌드에서는 에디터 프로세스의 부하를 제외하고 대상 기기에서 자세히 분석할 수 있습니다. 다만 어느 쪽의 결과든 그대로 출시 빌드의 성능으로 볼 수는 없습니다.

에디터에서 조사 진단용 빌드로 실기기 분석 측정용 빌드로 검증

결과와 함께 빌드 구성을 기록합니다. 구성이 달라지면 부하뿐 아니라 측정 대상의 동작도 달라질 수 있습니다.

에디터 창과 에셋 처리, 다른 도구도 같은 기기의 자원을 사용합니다. 진단용 빌드에는 추가 코드와 프로파일링에 따른 부하가 생길 수 있습니다. 차이는 엔진, 백엔드, 플랫폼, 활성화한 옵션에 따라 달라지므로 일률적으로 빼도 되는 값은 없습니다. 진단용 빌드에서 찾은 고비용 함수를 조사하되, 수정 효과는 플레이어가 실행할 빌드에서도 확인해야 합니다.

코드를 바꾸기 전에 실행 조건 맞추기

빌드 설정만으로 재현 가능한 비교가 되지는 않습니다. 먼저 저장된 게임을 불러와 정해진 카메라 경로를 따라가는 식으로 시나리오 하나를 정하고, 매번 결과와 함께 다음 조건을 기록합니다.

  • 빌드 ID와 커밋, 엔진 버전, 스크립팅 백엔드, 빌드 구성
  • 기기, GPU 드라이버, 해상도, 품질 설정, VSync, 프레임 속도 제한
  • 씬, 시작 상태, 입력이나 이동 경로, 측정 시간 또는 프레임 수
  • 워밍업 방식과 캐시 상태, 특히 셰이더 컴파일과 최초 로딩의 처리 방식
  • 활성화한 측정 코드, 샘플링 간격, 누락되거나 버려진 샘플

변경 후 빌드를 테스트하기 전에 기준 빌드부터 여러 번 실행합니다. 기준 빌드의 반복 실행 결과가 기대하는 개선 폭만큼 흔들린다면 테스트 조건을 먼저 점검해야 합니다. 반복 실행은 변동을 확인하는 데 도움이 되지만, 그것만으로 통계적 신뢰성이 확보되지는 않습니다. 스마트폰과 노트북에서는 전원과 온도 조건도 비슷하게 유지합니다.

최초 로딩과 캐시가 준비된 뒤의 플레이는 따로 테스트합니다. 안정 상태를 측정할 때는 셰이더 컴파일을 측정 구간에서 제외할 수 있습니다. 하지만 첫 실행의 끊김을 조사하면서 같은 처리를 하면 정작 확인하려는 문제를 빼게 됩니다.

엔진별 측정용 빌드 설정

Unreal Engine: 지원되는 엔진에서 Test 구성 사용하기

Epic은 Test를 일부 콘솔 명령, 통계, 프로파일링 도구를 활성화한 Shipping 구성으로 설명합니다. 출시용 최적화와 진단 기능이 함께 필요할 때 선택할 수 있습니다. Development도 대부분의 최적화를 활성화하므로, 최적화하지 않은 Debug 빌드와 같다고 보면 안 됩니다.

먼저 설치된 엔진이 Test를 지원하는지 확인합니다. Installed Build의 기본 구성은 Shipping, Development, DebugGame입니다. Test를 사용하려면 보통 소스에서 빌드한 엔진이나 Test가 포함된 커스텀 Installed Build가 필요합니다. 명령에서 Test를 선택하는 것만으로 빠진 바이너리가 생기지는 않습니다.

지원되는 엔진에서 Windows용으로 패키징하는 명령 예시입니다. 프로젝트 경로를 바꾸고, 설치된 엔진의 Automation Tool 실행 스크립트를 사용합니다.

RunUAT.bat BuildCookRun -project="C:/Path/MyGame.uproject" -platform=Win64 -clientconfig=Test -cook -stage -pak -package -build

생성된 패키지에서 필요한 진단 명령과 카운터를 실제로 쓸 수 있는지 확인합니다. Test와 Shipping은 기본적으로 컴파일할 때 로그 출력을 제외하므로, UE_LOG를 쓸 수 있다고 가정해 측정을 설계하지 마세요. 이 설정을 바꾸려면 엔진을 다시 빌드해야 할 수도 있습니다. 측정 코드는 프로젝트에서 필요한 부분에만 넣습니다. Test를 쓸 수 없어 Development로 조사하더라도, 마지막에는 프로젝트에서 지원하는 측정 수단으로 Shipping에서 효과를 검증합니다.

Unity: 출시 버전과 백엔드·컴파일러 설정 맞추기

Unity 6에서는 전용 빌드 프로파일을 만들고, 출시 환경에 가까운 조건에서 비교할 때 Development Build를 끕니다. 출시 버전이 IL2CPP라면 IL2CPP를, Mono라면 Mono를 사용합니다. IL2CPP의 Release 또는 Master 설정도 출시 버전과 맞춥니다. 코드 수정과 동시에 백엔드까지 바꾸면 비교에 다른 변수가 들어갑니다.

Unity Profiler로 조사할 Development 프로파일은 별도로 유지합니다. 프로파일러 연결과 Deep Profiling은 진단용 설정이므로, 출시 성능을 비교할 때 무심코 켜 둔 채 측정하지 않도록 합니다. Unity 6 이전 버전에서도 BuildPipeline 스크립트로 구성을 나눌 수 있습니다.

프로파일별 스크립팅 정의 심볼을 사용하면 측정용 빌드에서만 진단 마커를 남길 수 있습니다.

using System.Diagnostics;

public static class Measure
{
    [Conditional("MEASUREMENT")]
    public static void Mark(string label)
    {
        UnityEngine.Debug.Log($"[measure] {label} @ {UnityEngine.Time.frameCount}");
    }
}

프로파일의 Scripting Defines에 MEASUREMENT를 추가하고 시나리오의 시작과 끝 같은 지점에서 Measure.Mark를 호출합니다. 이 예시는 구간을 표시하는 로그이지 프레임 시간을 수집하는 코드는 아닙니다. 문자열 포맷팅과 로그 기록에도 비용이 드니 매 프레임 호출하지 마세요. [Conditional]은 심볼이 없을 때 호출 코드를 제외하지만, 메서드 본문 자체를 어셈블리에서 제거하지는 않습니다.

Godot: 릴리스 내보내기에 측정용 기능 태그 추가하기

내보내기 프리셋을 복제하고 Export With Debug를 끕니다. 렌더러, 플랫폼, 내보내기 템플릿은 출시 예정인 구성에 맞춥니다. 측정용 프리셋을 만들기 위해 엔진의 커스텀 템플릿까지 빌드할 필요는 없습니다.

프리셋에 measurement 같은 커스텀 기능을 추가합니다.

custom_features="measurement"

C# 프로젝트에서는 이 태그로 시나리오 시작 지점을 기록할 수 있습니다.

if (OS.HasFeature("measurement"))
{
    GD.Print($"[measure] scenario-start frame={Engine.GetFramesDrawn()}");
}

커스텀 기능 태그는 내보낸 프로젝트에서만 적용되며, 일반적인 에디터 실행에서는 적용되지 않습니다. 내보낸 실행 파일에서 동작을 확인하세요. Unity 예시와 마찬가지로 이 마커는 시나리오의 한 지점을 표시할 뿐이므로 지표를 수집하는 코드는 따로 필요합니다. 수집 비용과 설정도 비교할 실행 간에 동일하게 유지합니다.

실제 수집한 단위에 맞춰 지표 읽기

60 FPS가 목표라면 프레임당 시간 예산은 약 16.67 ms, 30 FPS라면 약 33.33 ms입니다. 평균 FPS만 보지 말고 프레임 시간의 분포를 확인합니다. P95는 관측값의 95%가 그 이하에 해당하는 값입니다. 더 드물게 발생하는 심한 끊김은 놓칠 수 있으므로, 필요에 따라 더 높은 백분위수, 프레임 시간 예산을 넘은 횟수, 타임라인도 살펴봅니다.

무엇을 집계한 P95인지도 중요합니다. 주기적으로 수집한 텔레메트리 샘플의 P95는 그 샘플들의 백분위수이며, 렌더링한 모든 프레임의 P95와 같다고 볼 수는 없습니다. 두 보고서를 비교하기 전에 샘플링 방식을 기록합니다.

프레임 시간측정 출처와 구간을 기록합니다. 평소의 프레임과 오래 걸린 프레임을 함께 봅니다.
메모리카운터 이름과 단위를 명시합니다. 피크와 로드·언로드 반복에 따른 변화를 확인합니다. 값이 높다는 것만으로 누수를 단정할 수는 없습니다.
로딩 시간시작과 완료의 기준을 고정합니다. 최초 로딩과 캐시가 준비된 뒤의 로딩을 나눠 비교합니다.
스토리지 I/O읽기와 대기를 프레임 타임라인에 맞춰 봅니다. 처리량만으로 끊김의 원인을 알 수는 없습니다.

위치에 따라 발생하는 문제라면 씬, 좌표, 카메라 방향도 기록합니다. 세션 전체 평균보다 “입구 근처에서 오브젝트가 많은 방을 바라볼 때 느린 프레임이 발생했다”는 정보가 재현에 도움이 됩니다. 다만 그 위치의 어느 오브젝트나 시스템이 부하를 일으켰는지는 별도로 조사해야 합니다.

원인을 찾은 뒤 원래 조건으로 다시 검증하기

기록한 조건에 맞춰 문제를 재현하고 Unity Profiler, Memory Profiler, Unreal Insights, Godot 프로파일러로 조사합니다. Godot의 내장 스크립트 프로파일러는 GDScript용이므로, C# 코드의 세부 동작을 조사할 때는 적절한 .NET 프로파일러를 사용합니다.

부하 원인으로 의심되는 부분 하나를 수정한 뒤, 측정용 빌드에서 원래 시나리오를 다시 실행합니다. 여러 번 실행해도 개선 효과가 유지되는지, 메모리나 로딩 시간 또는 다른 프레임 처리가 나빠지지는 않았는지 확인합니다. 진단용 트레이스에서 빨라진 것만으로 검증이 끝난 것은 아닙니다.

측정 결과는 이펙트나 오브젝트를 더 넣을지 판단할 때도 도움이 됩니다. 프레임 시간 예산에 여유가 있는 씬이라면 추가를 검토할 수 있습니다. 하지만 렌더 스레드에 여유가 있다고 GPU 시간과 메모리에도 여유가 있는 것은 아닙니다. 추가한 상태를 같은 대상 기기와 조건에서 측정해야 합니다.

Framedash에 비교 조건과 결과 남기기

Framedash의 대시보드 히트맵에서는 수집한 성능 샘플을 맵에서 확인하고 빌드, 플랫폼, 기록된 속성으로 필터링할 수 있습니다. SDK 설정과 지원하는 수집기는 엔진마다 다릅니다. Unity, UE5, Godot C# 문서를 참고하고, 차트를 사용하기 전에 대상 빌드가 실제로 어떤 지표를 보내는지 확인하세요.

Unity에서는 성능 실행 비교 파일럿으로 시작할 수 있습니다. 자신의 PC에서 기준 실행, 변경 없이 반복한 실행, 변경 후 실행을 수집한 뒤 조건과 프레임 간격 요약을 확인하는 절차입니다. 맵은 필요하지 않습니다. 이 간격은 SDK의 Update 콜백 사이 시간을 측정하며, GPU 작업 완료나 화면 표시 시점의 간격은 아닙니다. 파일럿은 비교 가능한 근거가 갖춰졌는지 보여 주며, 성능 회귀 여부를 자동으로 판정하지는 않습니다.

재현 가능한 시나리오 하나와 효과를 확인할 변경 하나부터 고릅니다. 기준 결과와 실행 조건을 변경 후 결과와 함께 남기면 다음에도 같은 기준으로 비교할 수 있습니다.