Version: 2020.2
언어: 한국어
애플리케이션 프로파일링
Unity 최적화에 대한 이해

로우레벨 네이티브 플러그인 프로파일러 API

Unity 프로파일러는 애플리케이션의 성능을 분석하고 개선하는 데 사용할 수 있는 툴입니다. 프로파일러는 애플리케이션의 네이티브 Unity 코드와 관리되는 코드의 함수 호출에 대한 프레임 시간 분석을 수행합니다. ProfilerMarker API를 사용하여 자체 코드에 타이밍 마커를 삽입하면 애플리케이션의 특정 영역에 대한 성능을 더욱 자세히 파악할 수 있습니다.

로우 레벨 네이티브 플러그인 프로파일러 API를 사용하여 프로파일러를 확장하고 네이티브 플러그인 코드의 성능을 프로파일링하거나, 또는 Razor(PS4), PIX(Xbox, Windows), Chrome Tracing, ETW, ITT, Vtune, Telemetry 등의 타사 프로파일링 툴로 전송할 프로파일링 데이터를 준비할 수 있습니다. 로우 레벨 네이티브 플러그인 프로파일러 API는 Unity 프로파일러와 외부 툴 간의 통신을 위해 다음의 인터페이스를 제공합니다.

  • IUnityProfiler: 이 인터페이스를 사용하여 C/C++ 네이티브 플러그인 코드에서 Unity 프로파일러에 계측 이벤트를 추가합니다.
  • IUnityProfilerCallbacks: 이 인터페이스를 사용하여 Unity 프로파일러 이벤트를 인터셉트해서 저장하거나 다른 툴로 리디렉션합니다.

IUnityProfiler API

IUnityProfiler 플러그인 API를 사용하여 네이티브 플러그인의 C/C++ 코드에 계측을 추가합니다.

플러그인 API는 IUnityProfiler.h 헤더에서 선언된 IUnityProfiler 인터페이스로 표시됩니다. Unity는 이 헤더를 Unity 설치의 <UnityInstallPath>\Editor\Data\PluginAPI 폴더에 저장합니다. (macOS에서는 Unity 애플리케이션을 마우스 오른쪽 버튼으로 클릭한 후 Show Package Contents를 선택하십시오. 헤더는 Contents\PluginAPI에 있습니다.)

메서드 설명
CreateMarker 명명된 계측 범위를 나타내는 프로파일러 마커를 만듭니다. 이 마커는 계측 샘플을 생성하는 데 사용할 수 있습니다.
SetMarkerMetadataName 프로파일러 마커에 대한 계측 샘플과 함께 전달할 수 있는 커스텀 파라미터 이름을 지정합니다.
BeginSample 프로파일러 마커의 이름을 따라 명명한 코드의 계측 섹션을 시작합니다.
EndSample 계측 섹션을 종료합니다.
EmitEvent 메타데이터가 포함된 일반 이벤트를 방출합니다.
IsEnabled 프로파일러가 데이터를 캡처하는 경우 1을 반환합니다.
IsAvailable 프로파일러를 사용할 수 있는 에디터 또는 개발 플레이어의 경우 1을 반환하고, 릴리스 플레이어의 경우 0을 반환합니다.
RegisterThread 지정된 이름으로 현재 스레드를 등록합니다.
UnregisterThread 프로파일러에서 현재 스레드의 등록을 해제합니다.

사용 예제

이 예제는 프로파일러 창에 표시되는 프로파일러 이벤트를 생성합니다.

# include <IUnityInterface.h>
# include <IUnityProfiler.h>

static IUnityProfiler* s_UnityProfiler = NULL;
static const UnityProfilerMarkerDesc* s_MyPluginMarker = NULL;
static bool s_IsDevelopmentBuild = false;

static void MyPluginWorkMethod()
{
    if (s_IsDevelopmentBuild)
        s_UnityProfiler->BeginSample(s_MyPluginMarker);

    // Code I want to see in Unity Profiler as "MyPluginMethod".
    // ...

    if (s_IsDevelopmentBuild)
        s_UnityProfiler->EndSample(s_MyPluginMarker);
}

extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
    s_UnityProfiler = unityInterfaces->Get<IUnityProfiler>();
    if (s_UnityProfiler == NULL)
        return;
    s_IsDevelopmentBuild = s_UnityProfiler->IsAvailable() != 0;
    s_UnityProfiler->CreateMarker(&s_MyPluginMarker, "MyPluginMethod", kUnityProfilerCategoryOther, kUnityProfilerMarkerFlagDefault, 0);
}

extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
{
    s_UnityProfiler  = NULL;
}

IUnityProfilerCallbacks API 콜백

네이티브 프로파일러 플러그인 API는 Unity 보조 시스템과 타사 프로파일링 API 간의 인터페이스를 제공합니다. 이를 통해 외부 프로파일링 툴을 사용하여 Unity 애플리케이션을 프로파일링할 수 있습니다. IUnityProfilerCallbacks 헤더가 이 API를 노출하며, Unity는 이 API를 Unity 설치의 <UnityInstallPath>\Editor\Data\PluginAPI 폴더에 저장합니다. (macOS에서는 Unity 애플리케이션을 마우스 오른쪽 버튼으로 클릭한 후 Show Package Contents를 선택하십시오. 헤더는 Contents\PluginAPI에 있습니다.)

다음 Unity 프로파일러 기능을 사용하면 애플리케이션의 성능을 분석하는 데 도움이 되는 계측 데이터를 캡처할 수 있습니다.

프로파일러 기능 설명
카테고리 Unity는 프로파일 데이터를 카테고리(예: 렌더링, 스크립팅, 애니메이션)로 그룹화한 후 각 카테고리에 컬러를 할당합니다. 카테고리에 컬러를 할당하면 프로파일러 창에서 데이터 타입을 시각적으로 쉽게 구분할 수 있습니다. 프로파일러 네이티브 플러그인 API를 이용하면 이러한 컬러를 검색해서 가져온 후 외부 프로파일링 툴에서 사용할 수 있습니다.
사용 플래그 사용 플래그는 Unity가 외부 프로파일링 툴로 전송하는 데이터의 양을 줄이는 필터 역할을 합니다. 사용 플래그를 사용하면 Unity가 외부 툴로 전송하기 전에 프로파일링 데이터에서 불필요한 정보를 제거할 수 있습니다. 프로파일러는 데이터 필터링을 위해 이벤트 마커에 다음의 사용 플래그를 적용합니다.

유효성 플래그 - Unity 에디터, 개발 플레이어 또는 릴리스 플레이어에서 마커의 이용 가능 여부를 나타내는 플래그입니다.

상세도 수준 - 에디터에서 수행하는 작업 타입과 관련되며, 작업에 필요한 정보 레벨(예: 내부, 디버그 또는 사용자 수준)입니다.
프레임 이벤트 프로파일러 네이티브 플러그인 API를 사용하면 외부 프로파일링 툴에서 프레임 시간 분석을 수행할 수 있습니다.
스레드 프로파일링 Unity는 스레드(예: 메인 스레드, 렌더 스레드 및 작업 시스템 워커 스레드)에 대해 상당한 양의 작업을 수행합니다. 프로파일러 네이티브 플러그인 API를 사용하면 모든 스레드에 대한 프로파일링을 활성화할 수 있습니다.

Unity 프로파일러가 외부 프로파일러에서 생성하는 계측 데이터를 사용하려면 타사 프로파일러를 통합하는 C/C++ 플러그인 코드에서 다음의 최소 콜백 집합을 사용하십시오.

콜백 기능
RegisterCreateCategoryCallback IUnityProfilerCreateCategoryCallback 콜백을 등록하여 Unity가 카테고리를 생성할 때마다 프로파일러 카테고리 이름과 컬러를 가져옵니다.
RegisterCreateMarkerCallback Unity가 마커를 생성할 때마다 호출되는 IUnityProfilerCreateMarkerCallback 콜백을 등록합니다. 이 콜백을 사용하여 마커의 이름, 프로파일러 카테고리 및 사용 플래그를 가져오십시오. 콜백 함수의 const UnityProfilerMarkerDesc* markerDesc 파라미터는 RegisterMarkerEventCallback에서 마커를 필터링하는 데 사용할 수 있는 마커 설명의 영구 포인터를 나타냅니다.
RegisterMarkerEventCallback 싱글샷, 범위 지정, 메모리 할당 또는 가비지 컬렉션 이벤트가 발생할 때 Unity가 호출하는 IUnityProfilerMarkerEventCallback 콜백을 등록합니다. 이 콜백을 사용하여 외부 프로파일링 툴에서 관련 함수를 호출할 수 있습니다. 참고: Unity는 GC.Alloc 마커로 메모리 할당 이벤트를 나타내고, GC.Collect 마커로 가비지 컬렉션 이벤트를 나타냅니다.
RegisterFrameCallback 프레임을 사용하지 않는 외부 프로파일링 툴이 샘플을 사용할 수 있도록 샘플을 논리 프레임으로 캡슐화합니다. 또한 Unity가 다음 논리 CPU 프레임을 시작할 때 Unity 프로파일러가 실행하는 콜백도 등록합니다.
RegisterCreateThreadCallback Unity가 프로파일링을 위해 스레드를 등록할 때마다 내부 스레드 이름을 가져오는 콜백을 등록합니다.

사용 예제

이 예제는 푸시/표시 시맨틱이 있는 다른 프로파일러에 Unity 프로파일러 이벤트를 전달하는 방법을 보여줍니다. 다음의 두 가지 함수를 제공합니다.

  • void MyProfilerPushMarker(const char* name) - 명명된 마커를 푸시합니다.
  • void MyProfilerPopMarker() - 계측 마커를 표시합니다.

다음 예제는 Unity 프로파일러에서 외부 프로파일러로 계측 시작 및 종료 이벤트를 전달하는 데 필요한 최소한의 구현을 제공합니다.

# include <IUnityInterface.h>
# include <IUnityProfilerCallbacks.h>

static IUnityProfilerCallbacks* s_UnityProfilerCallbacks = NULL;

static void UNITY_INTERFACE_API MyProfilerEventCallback(const UnityProfilerMarkerDesc* markerDesc, UnityProfilerMarkerEventType eventType, unsigned short eventDataCount, const UnityProfilerMarkerData* eventData, void* userData)
{
    switch (eventType)
    {
        case kUnityProfilerMarkerEventTypeBegin:
        {
            MyProfilerPushMarker(markerDesc->name);
            break;
        }
        case kUnityProfilerMarkerEventTypeEnd:
        {
            MyProfilerPopMarker();
            break;
        }
    }
}

static void UNITY_INTERFACE_API MyProfilerCreateMarkerCallback(const UnityProfilerMarkerDesc* markerDesc, void* userData)
{
    s_UnityProfilerCallbacks->RegisterMarkerEventCallback(markerDesc, MyProfilerEventCallback, NULL);
}

extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
    s_UnityProfilerCallbacks = unityInterfaces->Get<IUnityProfilerCallbacks>();
    s_UnityProfilerCallbacks->RegisterCreateMarkerCallback(&MyProfilerCreateMarkerCallback, NULL);
}

extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
{
    s_UnityProfilerCallbacks->UnregisterCreateMarkerCallback(&MyProfilerCreateMarkerCallback, NULL);
    s_UnityProfilerCallbacks->UnregisterMarkerEventCallback(NULL, &MyProfilerEventCallback, NULL);
}

참고: 모든 마커에서 특정 콜백을 등록 해제하려면 첫 번째 파라미터가 null로 설정된 UnregisterEventCallback을 실행하십시오.

UnitySystracePlugin 예시

프레임마다 한 번 마커 콜백을 동적으로 등록하거나 등록 해제할 수 있습니다. 다음 예제는 타사 프로파일 상태에 따라 콜백을 활성화하거나 비활성화하여 프로파일링 오버헤드를 최소화합니다.

| static void UNITY_INTERFACE_API SystraceFrameCallback(void* userData)
{
    bool isCapturing = ATrace_isEnabled();
    if (isCapturing != s_isCapturing)
    {
        s_isCapturing = isCapturing;
        if (isCapturing)
        {
            s_UnityProfilerCallbacks->
              RegisterCreateMarkerCallback(SystraceCreateEventCallback, NULL);
        }
        else
        {
            s_UnityProfilerCallbacks->
              UnregisterCreateMarkerCallback(SystraceCreateEventCallback, NULL);
            s_UnityProfilerCallbacks->
              UnregisterMarkerEventCallback(NULL, SystraceEventCallback, NULL);
        }
    }
}

참고: 모든 마커에서 특정 콜백을 등록 해제하려면 첫 번째 파라미터가 null로 설정된 UnregisterEventCallback을 실행하십시오.

특수 마커

Unity는 유용한 메타데이터가 들어 있는 다음의 특수 마커를 제공합니다.

  • Profiler.DefaultMarker
  • GC.Alloc

Profiler.DefaultMarker

Profiler.DefaultMarker는 Unity가 Profiler.BeginSampleProfiler.EndSample 이벤트를 위해 예약하는 마커입니다.

위 예시에서 kUnityProfilerMarkerEventTypeBegin eventTypeProfiler.BeginSample 이벤트에 해당하며, 다음 데이터를 포함합니다.

  • Int32: UnityEngine.Object 인스턴스 ID입니다. 지정된 오브젝트가 없으면 0입니다.
  • UInt16 배열: Profiler.BeginSample에 전달되는 UTF16 문자열입니다. 크기는 바이트 단위입니다.
  • UInt32: 카테고리 인덱스입니다.

GC.Alloc

GC.Alloc는 가비지 컬렉션 할당에 해당하는 마커입니다. 다음의 데이터를 포함합니다.

  • Int64: 할당 크기입니다.
애플리케이션 프로파일링
Unity 최적화에 대한 이해