# 프로파일러 트레이스 분석

> Investigate common startup and runtime performance issues by analyzing Profiler traces.

애플리케이션을 프로파일링할 때 흔히 발생할 수 있는 몇 가지 문제가 있습니다. 이 페이지에서는 몇 가지 일반적인 성능 문제의 원인을 조사하는 방법을 간략하게 설명합니다.

## 시작 트레이스 분석

시작 시간을 추적할 때는 두 가지 주요 메서드, 즉 `UnityInitApplicationGraphics` 및 `UnityLoadApplication`을 검사해야 합니다. 이 두 가지 메서드는 프로젝트의 설정, 에셋, 코드가 시작 시간에 영향을 미칠 수 있는 주요 부분입니다.

**참고:** 애플리케이션의 시작 시간은 플랫폼마다 다릅니다. 대부분의 플랫폼에서는 [스플래시 화면](/engine/6000.0/manual/unity-editor/editor-features/comp-manager-group/class-player-settings/splash-screen.md)이 표시되는 동안 시작됩니다.


**iOS 기기에서 실행 중인 예시 Unity 프로젝트의 Instruments 트레이스:**
![](/api/media?file=/engine/6000.0/media/images/UnderstandingPerformanceinUnity-ProfilingSection_image_0.png)

위의 iOS 기기에서 실행되는 예시 Unity 프로젝트의 Instruments 트레이스 스크린샷에서 플랫폼별 `startUnity` 메서드의 `UnityInitApplicationGraphics`와 `UnityLoadApplication` 메서드에 주목하십시오.

`UnityInitApplicationGraphics`는 그래픽스 기기를 설정하고 Unity의 많은 내부 시스템을 초기화하는 등 많은 내부 작업을 수행합니다. 또한 리소스 시스템에 포함된 모든 파일의 인덱스를 로드하여 [리소스 시스템](/engine/6000.0/manual/analysis/understanding-performance/resources-folder.md)을 초기화합니다.

Unity의 [리소스 시스템](/engine/6000.0/script-reference/unity-engine/resources.md)은 프로젝트의 `Assets` 폴더에 있는 `Resources` 폴더의 데이터에 모든 [에셋 파일](/engine/6000.0/manual/working-in-unity/assets-and-media/asset-workflow/asset-types.md)을 포함합니다. 여기에는 `Resources` 폴더의 자식 폴더에 있는 모든 파일이 포함됩니다. 따라서 리소스 시스템을 초기화하는 데 소요되는 시간은 애플리케이션 프로젝트의 `Resources` 폴더 내 파일 수에 따라 증가합니다.

`UnityLoadApplication`에는 프로젝트의 첫 번째 씬을 로드하고 초기화하는 메서드가 포함되어 있습니다. 여기에는 셰이더 컴파일, 텍스처 업로드, 게임 오브젝트 인스턴스화 등 첫 번째 씬을 표시하는 데 필요한 데이터의 역직렬화 및 인스턴스화가 포함됩니다. 또한 Unity는 첫 번째 씬에 있는 모든 `MonoBehaviour`의 `Awake` 콜백을 실행합니다.

이러한 프로세스는 프로젝트의 첫 번째 씬에 있는 `Awake` 콜백에 오래 실행되는 코드가 있으면 해당 코드가 프로젝트의 초기 시작 시간을 늦추는 원인이 될 수 있음을 의미합니다. 이 문제를 해결하려면 슬로우 코드를 제거하거나 애플리케이션 라이프사이클의 다른 곳에서 실행해야 합니다.

## 런타임 트레이스 분석

초기 시작 시간 이후에 캡처된 프로파일링 트레이스의 경우, `PlayerLoop` 메서드가 가장 중요합니다. 이는 Unity의 메인 루프이며, 그 안에 있는 코드는 프레임당 한 번 실행됩니다.


**예시 Unity 프로젝트의 Instruments 트레이스:**
![](/api/media?file=/engine/6000.0/media/images/UnderstandingPerformanceinUnity-ProfilingSection_image_1.png)

위 스크린샷은 `PlayerLoop` 내에서 성능에 가장 큰 영향을 미치는 몇 가지 메서드를 보여 줍니다. **참고:** `PlayerLoop` 내 메서드 이름은 Unity 버전에 따라 다를 수 있습니다.

`PlayerRender`는 Unity의 렌더링 시스템을 실행하는 메서드입니다. 여기에는 오브젝트 컬링, 동적 배치 계산, GPU에 그리기 명령 제출이 포함됩니다. 이미지 효과나 렌더링 기반 스크립트 콜백(예: [OnWillRenderObject](/engine/6000.0/script-reference/unity-engine/mono-behaviour/on-will-render-object.md))도 여기에서 실행됩니다. 일반적으로 프로젝트가 인터랙티브 상태일 때 가장 많은 CPU 시간이 소모됩니다.

`BaseBehaviourManager`는 `CommonUpdate`의 세 가지 템플릿 버전을 호출합니다. 이는 현재 씬의 활성 게임 오브젝트에 연결된 `MonoBehaviour` 내 특정 콜백을 호출합니다.

* `CommonUpdate<UpdateManager>`는 `Update` 콜백 호출
* `CommonUpdate<LateUpdateManager>`는 `LateUpdate` 콜백 호출
* 물리 시스템이 선택되어 있으면 `CommonUpdate<FixedUpdateManager>`가 `FixedUpdate` 호출

일반적으로 `BaseBehaviourManager::CommonUpdate<UpdateManager>` 메서드는 검사해야 할 가장 유용한 메서드입니다. 이 메서드는 Unity 프로젝트 내에서 실행되는 대부분의 스크립트 코드에 대한 엔트리 지점이기 때문입니다.

다음과 같이 유용한 여러 다른 메서드도 있습니다.

* 프로젝트가 [UGUI 시스템](https://docs.unity3d.com/Packages/com.unity.ugui@latest)을 사용하는 경우 `UI::CanvasManager`가 여러 개의 다른 콜백을 호출합니다. 여기에는 프로파일러에 `CanvasManager`가 가장 자주 표시되는 두 가지 작업인 Unity UI의 일괄 계산 및 레이아웃 업데이트가 포함됩니다.
* `DelayedCallManager::Update`는 [코루틴](/engine/6000.0/manual/scripting/object-oriented-development/coroutines.md)을 실행합니다.
* `PhysicsManager::FixedUpdate`는 PhysX 물리 시스템을 실행합니다. 이는 주로 PhysX 내부 코드 실행을 포함합니다. 현재 씬의 물리 오브젝트(예: `Rigidbody` 및 `Collider`) 수는 PhysX의 내부 코드에 영향을 미칩니다. 여기에서도 특히 `OnTriggerStay` 및 `OnCollisionStay` 같은 물리 기반 콜백이 나타납니다.

프로젝트가 2D 물리 시스템을 사용하는 경우 이는 `Physics2DManager::FixedUpdate`에서 유사한 호출 집합으로 나타납니다.

## 스크립트 메서드 분석

IL2CPP로 크로스 컴파일된 플랫폼에서 스크립트를 호출할 때 `ScriptingInvocation` 오브젝트를 포함하는 트레이스 줄을 찾습니다. 여기에서 Unity의 내부 네이티브 코드가 스크립트 런타임으로 전환되어 스크립트 코드를 실행합니다. **참고:** 기술적으로 Unity가 IL2CPP를 통해 C# 코드를 실행하면 네이티브 코드가 됩니다. 그러나 이 크로스 컴파일된 코드는 주로 IL2CPP 런타임 프레임워크를 통해 메서드를 실행하며 수작업으로 작성된 C++과는 유사하지 않습니다.


**예시 Unity 프로젝트의 트레이스:**
![](/api/media?file=/engine/6000.0/media/images/UnderstandingPerformanceinUnity-ProfilingSection_image_2.png)

위의 스크린샷에서 `RuntimeInvoker_Void` 줄 아래에 중첩된 메서드는 Unity가 프레임당 한 번 실행한 크로스 컴파일된 C# 스크립트의 일부분입니다.

이 트레이스 줄의 이름은 원래 클래스의 이름 뒤에 밑줄이 있고 원래 메서드 이름을 붙인 것입니다. 이 예시 트레이스에서 `EventSystem.Update`, `PlayerShooting.Update`와 기타 여러 `Update` 메서드를 볼 수 있습니다. 이는 대부분의 `MonoBehaviours`에 있는 표준 Unity `Update` 콜백입니다.

이러한 메서드를 확장하여 어떤 메서드가 CPU 시간을 소비했는지 확인할 수 있습니다. 여기에는 프로젝트 내 다른 스크립트 메서드, Unity API, C# 라이브러리 코드가 포함됩니다.

위의 트레이스는 프레임당 한 번씩 전체 UI를 통해 레이캐스팅하는 `StandaloneInputModule.Process` 메서드를 보여 줍니다. 이 메서드는 터치 이벤트가 UI 요소 위에 머무르고 있는지, UI 요소를 활성화했는지를 감지합니다. 모든 UI 요소를 반복하여 마우스의 위치가 경계 직사각형 안에 있는지 테스트하는 이 메서드는 리소스를 많이 소모합니다.

## 에셋 로드

CPU 트레이스에서 에셋 로딩을 식별할 수도 있습니다. 에셋 로드를 나타내는 메인 메서드는 `SerializedFile::ReadObject`입니다. 이 메서드는 파일의 바이너리 데이터 스트림을 `Transfer`라는 메서드를 통해 작동하는 Unity의 직렬화 시스템에 연결합니다. `Transfer` 메서드는 텍스처, MonoBehaviour, 파티클 시스템 같은 모든 에셋 유형에 적용됩니다.


**씬 로딩 트레이스:**
![](/api/media?file=/engine/6000.0/media/images/UnderstandingPerformanceinUnity-ProfilingSection_image_3.png)

위 스크린샷은 Unity가 씬을 로드하는 트레이스입니다. 씬을 로드할 때 Unity는 `SerializedFile::ReadObject` 아래의 다양한 `Transfer` 메서드 호출에 따라 씬 내 모든 에셋을 읽고 역직렬화합니다.

런타임 중에 성능이 저하되고 성능 트레이스에 `SerializedFile::ReadObject`가 상당한 시간을 사용한 것으로 표시되면 에셋 로드로 인해 프레임 속도가 감소했다는 뜻입니다. **참고:** `SerializedFile::ReadObject`는 일반적으로 `SceneManager`, `Resources` 또는 AssetBundle API가 동기식 에셋 로드를 요청할 때 메인 스레드에 표시됩니다.

이러한 성능 저하를 해결하기 위해 에셋 로딩을 비동기식으로 만들거나(무거운 `ReadObject` 호출을 워커 스레드로 이동) 무거운 특정 에셋을 미리 로드할 수 있습니다.

`Transfer` 호출은 Unity가 오브젝트를 복제할 때도 표시됩니다(트레이스에 `CloneObject` 메서드로 표시됨). `CloneObject` 호출 아래에 `Transfer` 호출이 표시되면 Unity가 스토리지에서 에셋을 로드하지 않습니다. 대신 Unity는 이전 오브젝트의 데이터를 새 오브젝트로 전송합니다. 이를 위해 Unity는 이전 오브젝트를 직렬화하고 결과 데이터를 새 오브젝트로 역직렬화합니다.
