Unity의 코드는 애플리케이션에서 시간을 소비하는 항목에 대한 인사이트를 제공하는 수많은 프로파일러 마커로 계측됩니다. 다음 표에는 몇 가지 일반적인 마커에 대한 설명이 나와 있습니다.
메인 스레드 기본 마커는 애플리케이션에 소비된 시간과 에디터 및 프로파일러 활동에 소비된 시간을 명확하게 구분합니다. 또한 ProfilerRecorder는 이러한 샘플을 사용하여 메인 스레드의 프레임 타이밍을 확인할 수 있습니다.
마커 | 기능 |
---|---|
PlayerLoop | 애플리케이션의 기본 루프에서 생성된 모든 샘플이 포함되어 있습니다. 플레이어가 활성 상태의 플레이 모드로 에디터 내에서 실행되는 동안 플레이 모드 대신 에디터를 대상으로 하는 경우 PlayerLoop 샘플은 EditorLoop 아래에 중첩됩니다. |
EditorLoop (에디터 전용 마커) |
에디터의 기본 루프에서 생성된 모든 샘플이 포함되어 있습니다. 에디터에서 플레이어를 프로파일링할 때에만 나타납니다. 프로파일러로 플레이 모드를 대상으로 지정하면 EditorLoop 샘플은 플레이어가 포함된 에디터를 렌더링하고 실행하는 데 걸린 시간을 보여줍니다. 프로파일러는 EditorLoop 마커 아래에 더 자세한 데이터를 기록하지 않습니다. 이는 프로파일러가 플레이 모드를 프로파일링하는 동안 에디터 코드가 방출하는 샘플로 인해 프로파일링 부하가 높아지기 때문입니다. Unity는 CPU 프로파일러 모듈 차트에서 EditorLoop 마커의 샘플을 Others로 분류합니다. 따라서 일반적으로 EditorLoop 샘플은 해당 카테고리에 가장 크게 기여합니다. 이 시점에 에디터가 수행하는 작업을 확인하고 Others 카테고리에 기여하는 다른 항목에 대한 상세한 분석 데이터를 얻으려면 프로파일러 타겟을 에디터로 변경하십시오. 프로파일러의 타겟을 Editor로 전환하면 프로파일러가 CPU 프로파일러 모듈의 세부 정보 창에 표시하는 내용과 CPU 프로파일러 모듈 차트의 카테고리 요약이 변경됩니다. 이는 이전에 EditorLoop 샘플 아래에 숨겨져 있던 샘플이 해당 카테고리에 기여하기 때문입니다. |
Profiler.CollectEditorStats (에디터 전용 마커) |
다른 활성 프로파일러 모듈의 통계 수집과 관련된 샘플이 포함되어 있습니다. Profiler.CollectGlobalStats 마커 아래에 중첩된 샘플은 플레이어가 특정 모듈의 통계를 수집할 때 발생하는 오버헤드를 나타냅니다. 다른 모든 자식 샘플은 에디터에서만 효과를 반영합니다. 특정 모듈에서 발생하는 오버헤드를 제거하려면 모듈의 차트를 닫거나, Profiler.SetAreaEnabled를 호출하십시오. 참고: 빌트인 카운터를 사용하는 커스텀 프로파일러 모듈은 속해 있는 모듈이 비활성화된 경우에도 빌트인 카운터의 영역을 활성화합니다. 프로파일러가 이러한 통계를 수집하고 수집 오버헤드를 발생시키는 것을 방지하려면 빌트인 프로파일러 모듈과 커스텀 프로파일러 모듈이 모두 비활성화되어 있는지 확인하십시오. |
Unity 에디터에서 프로파일링할 때에만 나타나는 특정 마커가 있습니다. 이러한 마커는 플레이어 관련 활동에는 표시되지 않으며, 에디터 활동에만 관련됩니다. 에디터 전용 마커에는 GetComponentNullErrorWrapper(null 컴포넌트 사용 식별), CheckConsistency(오브젝트 설정의 유효성 검사), CheckAllowDestructionRecursive(파괴 검사), 그리고 프리팹 관련 활동이 포함됩니다.
기본적으로 CPU 프로파일러 모듈의 계층 구조 뷰에서 에디터 전용 마커가 있는 샘플 스택은 축소되고 EditorOnly [SampleName]
라는 이름이 지정됩니다. 이러한 샘플 스택 또는 자식 샘플은 가비지 컬렉션을 트리거하는 관리되는 할당을 유발할 수 있지만, 축소된 경우에는 부모 샘플의 GC.Alloc 값에 기여하지 않습니다.
기본 동작을 변경하려면 모듈 세부 정보 창의 오른쪽 상단에서 컨텍스트 메뉴를 선택하고 EditorOnly 샘플 축소 설정을 비활성화하십시오. 그러면 샘플을 확장하고 해당 GC.Alloc 값을 둘러싸는 마커에 제공할 수 있습니다.
이 옵션은 타임라인 뷰에 영향을 주지 않으며, 샘플과 해당 자식 샘플이 확장된 상태로 표시됩니다. 일반적으로 이러한 마커가 있는 샘플은 에디터 전용 활동과 관련되고 관리되는 할당 감소에 영향을 미치지 않으므로 무시할 수 있습니다. 하지만 플레이 모드에 대한 성능 영향에 심각한 문제가 있는지 조사할 때 유용할 수 있습니다.
잡 시스템을 사용하지 않는 경우 스크립팅 코드의 대부분이 다음 마커 아래에 중첩됩니다. 잡 시스템 샘플에 대한 자세한 내용은 이 페이지의 멀티 스레드 마커 섹션을 참조하십시오.
Unity의 업데이트 루프에 대한 자세한 내용은 이벤트 함수의 실행 순서 문서를 참조하십시오. PlayerLoop.SetPlayerLoop를 통해 자체 콜백을 플레이어 루프에 삽입했다면 스크립트 업데이트 샘플은 각 PlayerLoopSystem 마커 아래에 나타납니다(하위 시스템으로 입력된 경우 또는 자체적으로 메인 루프에 직접 입력된 경우).
마커 | 기능 |
---|---|
BehaviourUpdate | MonoBehaviour.Update 메서드의 모든 샘플이 포함되어 있습니다. |
CoroutinesDelayedCalls | 첫 번째 yield 이후 코루틴의 모든 샘플이 들어 있습니다. |
FixedBehaviourUpdate | Monobehaviour.FixedUpdate 메서드의 모든 샘플이 포함되어 있습니다. |
PreLateUpdate.ScriptRunBehaviourLateUpdate | Monobehaviour.LateUpdate 메서드의 모든 샘플이 포함되어 있습니다. |
Update.ScriptRunBehaviourUpdate | MonoBehaviour.Update와 코루틴의 모든 샘플이 포함되어 있습니다. |
이러한 마커에는 CPU가 GPU에 대한 데이터 처리에 시간을 소비하는 위치, 또는 GPU가 완료되기를 기다리는 위치가 포함되어 있습니다. GPU 프로파일러 모듈을 이용할 수 없거나, 리소스 소모가 과도해지는 경우에는 프로파일러 모듈 세부 정보 창의 툴바에 이러한 정보가 표시되지 않습니다. 이러한 마커 아래의 샘플은 애플리케이션이 CPU 바운드 또는 GPU 바운드인지 분명히 알려줍니다.
마커 | 기능 |
---|---|
WaitForTargetFPS | 애플리케이션이 Application.targetFrameRate에서 지정한 대상 FPS를 기다리는 데 소비한 시간을 나타냅니다. 샘플이 Gfx.WaitForPresentOnGfxThread의 하위 샘플인 경우에는 애플리케이션이 GPU를 기다리는 데 소비한 시간입니다. 예를 들어 QualitySettings.vSyncCount에서 설정되거나, vSync가 타겟 플랫폼에서 강제로 적용된 경우 GPU가 다음 VSync를 기다리는 데 소비한 시간일 수 있습니다. 하지만 이 마커가 있는 샘플은 GPU가 프레임 계산을 완료하지 않은 경우에도 방출됩니다. 이 마커가 있는 샘플이 많은 시간을 사용하는 원인을 파악하려면 CPU 프로파일러 모듈의 타임라인 뷰로 전환하십시오. 이 뷰에서는 렌더 스레드에서 발생한 상황과 현재 프레임에서 끝나는 이 샘플과 주변 프레임에서 끝나는 동일한 샘플 간에 경과한 시간을 확인할 수 있습니다. 대상 프레임 속도 또는 vSync를 기반으로 이 기간이 애플리케이션의 프레임 시간보다 길다면 프레임이 렌더링 또는 계산에 너무 많은 시간을 소비하는 것입니다. 이 경우 렌더링 스레드를 조사하고, 커맨드를 준비하여 GPU에 내리기 위해 수행한 다른 작업 대비 Gfx.PresentFrame에 소비한 시간을 확인하십시오. 렌더 스레드가 Gfx.PresentFrame에 많은 시간을 소비한 경우 렌더링 작업은 GPU 바운드입니다. 렌더 스레드의 시간이 커맨드 준비에 소비되었다면 애플리케이션은 CPU 바운드입니다. 무엇에 초점을 맞추는지 알아보려면 애플리케이션이 GPU 바운드인 경우 Unity 프로파일러 또는 플랫폼 프로파일러를 사용하여 GPU 작업을 프로파일링하십시오. 자세한 내용은 그래픽스 퍼포먼스 최적화에 대한 사용자 매뉴얼 문서를 참조하십시오. 참고: 에디터는 GPU에서 VSync를 수행하지 않고 대신에 WaitForTargetFPS를 사용하여 VSync 지연을 시뮬레이션합니다. Android, iOS 등과 같은 일부 플랫폼은 VSync를 강제로 적용하거나, 30 또는 60의 기본 프레임 속도 상한을 지정합니다. |
Gfx.PresentFrame | 애플리케이션이 GPU가 프레임을 렌더링하고 표시하기까지 기다린 시간을 표시하며, VSync 대기를 포함할 수 있습니다. 메인 스레드에서 WaitForTargetFPS 마커가 있는 샘플은 VSync를 기다리는 데 소비한 시간을 표시합니다. |
Gfx.ProcessCommands | 렌더 스레드의 모든 렌더링 커맨드 처리가 포함되어 있습니다. 애플리케이션이 메인 스레드에서 VSync 또는 새 커맨드를 기다리는 데 시간을 소비했을 수 있으며, 이는 해당 자식 샘플 Gfx.WaitForPresentOnGfxThread에서 확인할 수 있습니다. |
Gfx.WaitForCommands | 렌더 스레드가 새 커맨드에 대한 준비가 되었음을 나타냅니다. 이 마커가 표시되면 메인 스레드에서 병목 현상이 발생했음을 의미할 수 있습니다. |
<GraphicsAPIName>.WaitForLastPresent 예:GfxDeviceD3D11.WaitForLastPresent GfxDeviceD3D12.WaitForLastPresent GfxDeviceMetal.WaitForLastPresent |
이 마커가 있는 샘플은 메인 스레드가 GPU가 프레임 숫자를 화면으로 플립하기 위해 기다린 경우 나타납니다(Time.frameCount - QualitySettings.maxQueuedFrames + 1 ). 즉 ualitySettings.maxQueuedFrames가 1보다 크면 GPU가 이전 메인 스레드 프레임에서 렌더링하도록 요청한 프레임을 GPU가 플립할 때까지 기다리는 데 이 시간이 소요됩니다.이 샘플에 대한 자세한 내용과 Unity의 프레임 파이프라인 개요는 델타 시간 해결에 대한 Unity 블로그 포스트를 참조하십시오. |
Gfx.WaitForPresentOnGfxThread | 메인 스레드가 다음 프레임을 렌더링할 준비가 되었지만, 렌더 스레드가 GPU가 프레임 표시를 끝낼 때까지 기다리지 않았음을 의미합니다. 이는 애플리케이션이 GPU 바운드임을 표시할 수 있습니다. 렌더 스레드가 동시에 시간을 소비하는 곳을 보려면 CPU 프로파일러 모듈의 타임라인 뷰를 확인하십시오. 렌더 스레드가 Camera.Render에서 시간을 소비하면 애플리케이션은 CPU 바운드이며, 드로우 콜이나 텍스처를 GPU로 보내는 데 너무 많은 시간을 소비할 수 있습니다. 렌더 스레드가 Gfx.PresentFrame에서 시간을 소비하면 애플리케이션은 GPU 바운드이거나 GPU에서 VSync를 기다리는 중일 수 있습니다. Gfx.WaitForPresentOnGfxThread의 WaitForTargetFPS 하위 샘플은 Present 단계에서 애플리케이션이 VSync를 기다리는 데 소비한 시간을 나타냅니다. Present 단계는 Unity가 그래픽스 API에 버퍼 교환을 명령하는 시점과 이 작업이 완료된 시점 사이의 시간입니다. |
Gfx.WaitForRenderThread | 메인 스레드가 렌더 스레드가 현재 커맨드 스트림에 있는 모든 커맨드를 처리할 때까지 기다리고 있었음을 의미합니다. 이 마커가 있는 샘플은 멀티스레드 렌더링에서만 나타납니다. |
이 샘플은 Mono 또는 IL2CPP 스크립팅 백엔드 활동을 강조 표시하며, 가비지 컬렉션 및 할당 문제를 해결하는 데 유용합니다.
마커 | 기능 |
---|---|
GC.Alloc | 자동 가비지 컬렉션이 적용되는 관리되는 할당을 포함하는 관리되는 힙의 할당을 나타냅니다. 애플리케이션이 자동 가비지 컬렉션에 소비하는 시간을 줄이려면 이 타입의 샘플을 최소화해야 합니다. |
GC.Collect | 가비지 컬렉션과 관련된 샘플을 나타냅니다. Unity는 가비지 컬렉션을 수행해야 할 때마다 프로그램 코드 실행을 중지하고 가비지 컬렉터가 모든 작업을 완료한 경우에만 정상적인 실행을 재개합니다. 참고: 점진적 가비지 컬렉션을 활성화한 경우 가비지 컬렉터가 한 프레임만에 작업을 완료하지 못할 수 있습니다. 이러한 중단으로 인해 1밀리초 미만에서 수백 밀리초까지 지속되는 애플리케이션 실행 지연이 발생할 수 있습니다. 이는 가비지 컬렉터가 처리해야 하는 메모리의 양과 애플리케이션이 실행 중인 플랫폼에 따라 다릅니다. 자세한 내용은 자동 메모리 관리의 이해 문서를 참조하십시오. |
Mono.JIT Mono 전용 |
스크립팅 메서드의 JIT(Just-In-Time) 컴파일과 관련된 샘플이 포함되어 있습니다. 함수가 처음 실행될 때 Mono가 이를 컴파일하고 Mono.JIT는 이 컴파일 오버헤드를 나타냅니다. |
UnsafeUtility.Malloc | 관리되지 않는 메모리를 할당하기 위해 UnsafeUtility.Malloc을 호출하는 샘플이 포함되어 있습니다. 가비지 컬렉터는 이 메모리를 추적하지 않지만, 메모리를 할당하면 이 샘플에서 보이는 것과 같이 성능에 큰 영향을 미칠 수 있습니다. 이 호출의 소스를 조사하려면 프로파일러 창에서 이 마커에 대해 호출 스택 기록을 활성화하십시오. |
이 마커에는 소비된 CPU 주기를 측정하지 않고, 대신 스레드 동기화와 잡 시스템과 관련된 정보를 강조 표시하는 샘플이 포함되어 있습니다. 이러한 샘플이 표시되면 CPU 프로파일러 모듈의 타임라인 뷰에서 다른 스레드에서 동시에 무슨 일이 일어나고 있는지 확인하십시오.
마커 | 기능 |
---|---|
Idle | 워커 스레드가 비활성 상태인 시간을 나타내는 샘플이 포함되어 있습니다. 워커 스레드는 잡 시스템이 워커 스레드를 사용하지 않을 때마다 비활성화되고 대기 모드로 전환되어 세마포어에서 대기합니다. Idle 샘플 간의 작은 간극은 보통 잡 시스템이 샘플들을 깨울 때 발생합니다(예: 새로운 잡 예약). 간극이 길면 네이티브 잡이 실행 중인 스레드에서 계측되지 않았음을 의미합니다. |
JobHandle.Complete | 작업의 동기화 시점이 발생한 시기를 나타내는 샘플이 포함되어 있습니다. 동기화 시점은 애플리케이션 성능에 영향을 미칠 수 있으며 멀티스레드 잡 코드의 실행을 방해할 수 있습니다. 동기화 시점이 발생한 위치를 쉽게 찾으려면 이 샘플에 대해 호출 스택을 활성화하십시오. CPU 프로파일러 모듈의 타임라인 뷰에서 플로 이벤트를 활성화하여 이 시점에서 완료된 잡을 확인할 수 있습니다. |
Semaphore.WaitForSignal | 스레드의 동기화 시점을 설명하는 샘플이 포함되어 있습니다. 대기 중인 스레드를 찾으려면 바로 전에 종료된 샘플의 타임라인 뷰를 확인하십시오. |
WaitForJobGroupID | JobHandle의 Sync Fence가 트리거되었습니다. 이로 인해 작업 훔치기가 발생할 수 있습니다. 이는 워커가 해당 작업을 마치고 다른 워커가 작업을 끝마치기를 기다릴 때 발생합니다. 또한 이 마커에서 실행된 작업 샘플로 표시됩니다. “도난당한” 작업은 대개 기다리고 있던 작업이 아닙니다. |
다음 표에는 몇 가지 간략한 물리 프로파일러 샘플이 간략하게 소개되어 있습니다. FixedUpdate는 이러한 샘플을 모두 호출합니다.
마커 | 기능 | |||
---|---|---|---|---|
Physics.FetchResults | 컨택트 스트림, 트리거 오버랩, 조인트 파손 이벤트 등과 같은 물리 엔진의 물리 시뮬레이션 결과를 수집하는 샘플이 포함되어 있습니다. | |||
Physics.Interpolation | Physics.Interpolation 메서드의 실행 시간을 측정하는 샘플이 포함되어 있습니다. 이 메서드는 애플리케이션의 모든 물리 오브젝트에 대한 포지션 및 회전 보간을 관리합니다. | |||
Physics.Processing | 모든 스레드에서 물리 시뮬레이션이 완료될 때까지 메인 스레드에서 대기하는 데 시간을 소비한 샘플이 포함되어 있습니다. 애플리케이션이 Physics.Processing에서 많은 시간을 소비하지만, 씬에 몇 개의 물리 관련 게임 오브젝트만 있다면 워커 스레드가 작업 훔치기로 인해 다른 시스템 작업을 선택하고 물리를 보고했음을 나타낼 수 있습니다. 이는 대기하는 동안 메인 스레드가 우선 순위가 높은 대기열에서 작업을 선택하기 때문입니다. | |||
Physics.ProcessingCloth | Physics.ProcessingCloth 메서드의 실행 시간을 측정하는 샘플이 포함되어 있습니다. 이 메서드는 모든 천 물리 작업을 처리합니다. 이 샘플을 확장하면 물리 엔진에서 내부적으로 수행된 작업의 하위 수준 세부 정보가 표시됩니다. | |||
Physics.ProcessReports | OnTriggerEnter 등과 같은 콜백을 통해 스크립트에 물리 데이터를 전달하는 데 소요된 시간에 해당하는 샘플이 포함되어 있습니다. 참고: ** 이러한 샘플은 FetchResults 중에 이미 준비되었기 때문에 필요한 데이터를 계산하지 않습니다. 다음과 같은 네 가지 하위 단계가 있습니다.| ||Physics.Contacts|Physics.Contacts의 실행 시간을 측정하는 샘플이 포함되어 있습니다. OnCollisionEnter, OnCollisionExit 및 OnCollisionStay 이벤트를 처리합니다.| ||Physics.JointBreaks|Physics.JointBreaks의 실행 시간을 측정하는 샘플이 포함되어 있습니다. 끊어진 조인트와 관련된 업데이트와 메시지를 처리합니다.| ||Physics.TriggerEnterExits|Physics.TriggerEnterExits의 실행 시간을 측정하는 샘플이 포함되어 있습니다. OnTriggerEnter 및 OnTriggerExit 이벤트를 처리합니다.| ||Physics.TriggerStays|Physics.TriggerStays의 실행 시간을 측정하는 샘플이 포함되어 있습니다. OnTriggerStay 이벤트를 처리합니다.| |Physics.Simulate||Physics.Simulate 메서드의 전제 조건 작업에 소비된 시간을 측정하는 샘플이 포함되어 있습니다. 이 메서드는 물리 엔진이 시뮬레이션을 실행하도록 지시하여 현재 물리 상태를 업데이트합니다.| |Physics.UpdateBodies||모든 물리 바디의 포지션과 회전을 업데이트하는 샘플이 포함되어 있습니다. Rigidbody 컴포넌트가 있는 각 게임 오브젝트의 경우 이 마커가 있는 샘플은 물리 엔진에서 포즈를 읽고 Transform에 기록합니다.| |Physics.UpdateCloth** |
Physics.UpdateCloth 메서드의 실행 시간을 측정하는 샘플이 포함되어 있습니다. 이 메서드는 천과 해당 스킨드 메시와 관련된 업데이트를 처리합니다. |
스크립트 라이프사이클과 스크립트 라이프사이클 내 일반 샘플에 대한 자세한 내용은 이벤트 함수의 실행 순서 문서를 참조하십시오.
CPU 프로파일러는 몇 가지 일반적인 성능 문제를 감지하고 그에 대해 경고합니다. 이러한 경고는 모듈 세부 정보 창에서 CPU 프로파일러 모듈의 계층 구조 뷰의 경고 열에 나타납니다.
프로파일러는 성능이 중요한 상황에서 피해야 할 특정 호출을 감지할 수 있습니다. 또한 경고와 함께 해당 작업이 리소스를 많이 소모하는 이유를 표시합니다.
경고 | 설명 |
---|---|
Animation.DestroyAnimationClip Animation.AddClip Animation.RemoveClip Animation.Clone Animation.Deactivate |
RebuildInternalState가 트리거되었음을 나타냅니다. RebuildInternalState는 Animation 컴포넌트의 각 클립에 대한 커브 리스트를 살펴본 후 각 커브를 게임 오브젝트의 컴포넌트 값에 다시 바인딩하는 작업입니다. 리소스를 많이 소모하는 작업이므로, 런타임 시점에는 이러한 메서드를 최대한 호출하지 않아야 합니다. |
AssetBundle.asset/allAssets | AssetBundle 로드가 완료되지 않은 상태에서 Unity가 AssetBundleRequest.assets/allAssets API를 호출했음을 나타냅니다(AssetBundleRequest.isDone은 false임). 이로 인해 메인 스레드가 중단되고 로드 작업이 완료될 때까지 대기합니다. |
AsyncUploadManager.AsyncBufferResized AsyncUploadManager.AsyncBufferDelete |
GPU에 데이터를 업로드하기 위한 내부 버퍼가 충분히 크지 않아서 크기가 조정되었음을 나타냅니다. 이러한 크기 조정은 속도를 저하시키고 CPU 활동의 급증을 유발합니다. 더 큰 크기를 미리 할당하기 위해 메모리를 확보해두면 이러한 경고를 방지할 수 있습니다. 품질 설정의 Async Upload Buffer Size 설정을 사용하여 기본 크기를 설정할 수 있습니다. AsyncUploadManager.AsyncBufferResized 마커는 기본 버퍼 크기에 대한 가이드로 사용할 수 있는 신규 할당 크기를 나타냅니다. |
Rigidbody.SetKinematic | 리지드바디에 대해 볼록하지 않은 메시 콜라이더를 다시 생성합니다. |