https://dev.epicgames.com/documentation/ko-kr/unreal-engine/using-the-low-level-memory-tracker-in-unreal-engine?application_version=5.3

 

언리얼 엔진에서 로우 레벨 메모리 트래커 사용하기 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 프로젝트의 메모리 사용량을 트래킹합니다.

dev.epicgames.com

  • Unreal에서 제공하는 Memory 사용량 Tracking tool
  • Scoped-Tag System을 사용해 Unreal Engine과 OS에서 할당한 모든 Memory에 대한 Account를 유지
  • Unreal Engine에서 사용하는 모든 Platform에 대한 LLM을 지원

LLM Tracker

Default Tracker

  • Engine의 모든 Alloc에서 사용하는 High-Level Tracker
  • FMemory::Malloc을 통해 이루어진 할당을 기록
  • Stat LLM, Stat LLMFULL Console Command에 대한 통계를 제공
  • Platform Tracker의 Subset

Platform Tracker

  • OS에서 이루어진 모든 Alloc을 기록하는 Low-Level Tracker
    • BindMalloc
      • OS 단위에서 발생하는 Memory Allocation을 더 효율적으로 사용하기 위한 Wrapper
      • 큰 규모의 Memory를 OS로부터 할당 받고, Application에 작은 단위로 쪼개서 제공한다.
    • AnsiMalloc
      • 표준 C++ Library 기반 Allocator
    • TBBMalloc
      • Intel의 Thread Building Blocks 기반의 Allocator
    • JeMalloc
      • 성능 최적화가 강조된 Allocator

LLM Configuration

Command-line Argument

  • -LLM
    • LLM 활성화
  • -LLMCSV
    • 모든 값을 csv 파일에 지속적으로 기록
      • saved/profiling/llm/ 에 기록
      • csv 파일에는 현재 값(MB)을 표시하는 각 Tag에 대한 Row가 포함된다.
      • 기본적으로 5초마다 새 Row가 작성된다.
        • Console 변수인 LLM.LLMWriteInterval을 사용하여 주기를 변경할 수 있다.
    • -LLM이 자동으로 활성화
  • -llmtagsets=Assets
    • 각 Asset별 할당된 합계를 표시
    • 5.3 기준 실험 기능
  • -llmTagSets=ASsetClasses
    • 각 UObject Class Type의 합계를 표시
    • 5.3 기준 실험 기능

Console Command

  • stat LLM
    • LLM 요약을 표시
    • 모든 Low-Level Engine 통계는 단일 통계로 그룹화 된다.
  • stat LLMFULL
    • 모든 LLM 통계를 표시
  • stat LLMPlatform
    • OS에서 할당된 모든 Memory에 대한 통계를 표시
  • stat LLMOverhead
    • LLM에서 내부적으로 사용하는 Memory 표시

LLM Tag

  • Engine과 Game Code에 의해 이루어진 모든 Memory Allocation에는 속한 Category를 식별하는 Tag가 할당된다.
    • 즉, Memory는 한번만 Tracking 되고, 누락되지 않는 한 두 번 계산되지 않는다.
    • 모든 Category의 합계가 Game에서 사용하는 총 Memory에 추가 된다.
  • Tag는 Tag Scope Macro를 사용하여 적용된다.
    • 해당 Tag 범위 내에서 이루어진 모든 Allocation은 지정된 Tag가 제겅된다.
  • LLM은 Tag Scope Stack을 유지/관리하고, Allocation에 최상위 Tag를 적용한다.

Engine에서 제공하는 Tag Category

UObject

  • UObject에서 상속된 모든 Class와 Property를 포함하여 해당 Class에 의해 Serialize 된 모든 것을 포함
    • UObject는 어떤 Category에서도 Tracking 되지 않는 모든 Engine/Game Memory에 대한 포괄적 Tag
  • 별도로 Tracking되는 Mesh, Animation Data는 포함되지 않는다.
  • Level에 배치된 Object의 수에 대응

EngineMisc

  • 다른 Category에서 Tracking되지 않는 모든 Low-Level Memory

TaskGraphTasksMisc

  • 자체 Category가 없는 Task Graph에서 시작되는 모든 Task
  • 보통은 상당히 낮아야 한다.

StaticMesh

  • UStaticMesh Class 및 관련 Property
  • 실제 Mesh Data를 포함하지는 않는다.

Custom Tag

  • Memory Profiling 할 때 Memory Insight에서 LLM Untracked Tag가 지정된 Allocated Memory가 있을 수 있다.
    • 이 때 LLM_DECLARE_TAG, LLM_DEFINE_TAG 매크로를 이용해 Custom Tag를 만들어 대응한다.
  • 이 매크로들은 Engine File을 수정하지 않고 Game Modue이나 Plugin에서 수행할 수 있다.
  • 자세한 구현은 Runtime/Core/Public/HAL/LowLevelMemTracker.h에서 확인할 수 있다.

LLM_DECLARE_TAG

  • 더보기
    /**
    * Declare a tag defined by LLM_DEFINE_TAG. It is used in LLM_SCOPE_BYTAG or referenced by name in other LLM_SCOPEs.
    * @param UniqueName - The name of the tag for looking up by name, must be unique across all tags passed to
    *        LLM_DEFINE_TAG, LLM_SCOPE, or ELLMTag.
    * @param ModuleName - (Optional, only in LLM_DECLARE_TAG_API) - The MODULENAME_API symbol to use to declare module
    *        linkage, aka ENGINE_API. If omitted, no module linkage will be used and the tag will create link errors if
    *        used from another module.
    */
    #define LLM_DECLARE_TAG(UniqueNameWithUnderscores) extern FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)
    #define LLM_DECLARE_TAG_API(UniqueNameWithUnderscores, ModuleAPI) extern ModuleAPI FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)

LLM_DEFINE_TAG

  • 더보기
    /**
     * Define a tag which can be used in LLM_SCOPE_BYTAG or referenced by name in other LLM_SCOPEs.
     * @param UniqueNameWithUnderscores - Modified version of the name of the tag. Used for looking up by name,
     *        must be unique across all tags passed to LLM_DEFINE_TAG, LLM_SCOPE, or ELLMTag.
     *        The modification: the usual separator / for parents must be replaced with _ in LLM_DEFINE_TAGs.
     * @param DisplayName - (Optional) - The name to display when tracing the tag; joined with "/" to the name of its
     *        parent if it has a parent, or NAME_None to use the UniqueName.
     * @param ParentTagName - (Optional) - The unique name of the parent tag, or NAME_None if it has no parent.
     * @param StatName - (Optional) - The name of the stat to populate with this tag's amount when publishing LLM data each
     *        frame, or NAME_None if no stat should be populated.
     * @param SummaryStatName - (Optional) - The name of the stat group to add on this tag's amount when publishing LLM
     *        data each frame, or NAME_None if no stat group should be added to.
     */
    #define LLM_DEFINE_TAG(UniqueNameWithUnderscores, ...) FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)(TEXT(#UniqueNameWithUnderscores), ##__VA_ARGS__)

LLM_SCOPE_BYTAG

  • 더보기
    #define LLM_SCOPE_BYTAG(TagDeclName) 
    FLLMScope SCOPE_NAME(PREPROCESSOR_JOIN(LLMTagDeclaration_, TagDeclName).GetUniqueName(), false /* bIsStatTag */, ELLMTagSet::None, ELLMTracker::Default);\
    UE_MEMSCOPE(PREPROCESSOR_JOIN(LLMTagDeclaration_,TagDeclName).GetUniqueName());

Custom Tag macro 사용 방법

  1. LLM_DELCARE_TAG를 사용해 header File에 Custom LLM Tag를 선언
  2. 관련 .cpp File에서 LLM_DEFINE_TAG를 사용해 Custom LLM Tag를 정의
  3. LLM_SCOPE_BYTAG를 사용해 .cpp 파일의 Memory Tracking

Code 예시

  • CustomTagExample.h
    • 더보기
      #pragma once
      ...
      LLM_DECLARE_TAG(MyTestTag);
  • CustomTagExample.cpp
    • 더보기
      LLM_DEFINE_TAG(MyTestTag);
      
      AMyActor::AMyActor()
      {
          LLM_SCOPE_BYTAG(MyTestTag);
          MyLargeBuffer.Reset(new uint8[1024*1024*1024]);
      }

Tag Set

  • 실험 단계
  • LowLevelMemTracker.h안의 LLM_ALLOW_SSETS_TAGS를 정의하여 사용
  • Tag Set을 사용할 때 각 Allocation은 Asset이나 Object Class 이름을 추가로 저장한다.
  • Memory 사용량과 Runtime Performance에 모두 오버헤드가 추가된다.

Technical Detail Implementation

  • LLM은 Pointer를 Index로 하여 모든 Allocation의 Map을 유지하며 동작한다.
    • 이 Map은 각 Allocation의 크기와 할당된 Tag를 포함한다.
  • Game에는 동시에 최대 400만개의 Live Allocation을 가질 수 있다.
    • 때문에 Memory Overhead를 가능한 작게 유지하는 것이 중요하다.
  • 현재 구현에서는 Allocation 하나 당 21Byte를 사용한다.
    • Allocation Size
      Pointer 8 Byte
      Pointer Hash Key 4 Byte
      Size 4 Byte
      Tag 1 Byte
      Hash Map Index 4Byte
  • OnLowLevelAlloc 함수를 사용하여 Allocation을 Tracking하면
    Tack Stack 제일 위에 있는 Tag가 현재 Tag가 되고, Pointer를 Key로 사용해 그 Allocation이 Map에 저장이 된다.
  • Conflict를 피하기 위해 각 Tag의 Frame Delta는 별도의 FLLMThreadState Class Instance에서 Tracking 된다.
  • Frame이 끝나면 이 Delta를 합산해 통계 시스템과 .csv File에 게시한다.
  • LLM은 초기에 Initialize되므로 Default로 활성화 해야 한다.
    • Command-line에서 LLM이 활성화 되어있지 않으면 자동으로 종료가 된다.
    • 동시에 모든 Memory가 종료되어 Overhead가 발생하지 않는다.
  • LLM은 Test 및 Shipping Build에서 완전히 Compile 된다.
  • LLM은 통계 시스템 없이 실행할 수 있다.
    • 이 경우, 화면에 통계를 표시할 수는 없지만 여전히 .csv File에는 기록이 된다.
    • 이를 위해서는 LowLevelMemTracker.h에서 ENABLE_LOW_LEVEL_MEM_TRACKER를 수정해
      LLM을 활성화 해야 한다.
  • Tag는 Scope Macro를 사용하여 적용하는데, 이에는 2가지가 제공된다.
    • LLM_SCOPE
      • Default Tracker Scope 설정
    • LLM_PLATFORM_SCOPE
      • Platform Tracker Scope 설정
    • LLM_SCOPED_TAG_WITH_STAT
      • 위와 같이 통계를 사용하는 Scope Macro는 Deprecated 되었기에 사용하면 안된다.
  • LLM 내부에서 사용하는 모든 Memory는 Platform에서 제공하는 LLMAlloc/LLMFree 함수로 관리한다.
    • 때문에 LLM이 자체 Memory 사용량을 Tracking 하지 않도록,
      그리고 무한  Loop에 빠지지 않도록 다른 방식으로 할당하지 않는 것이 중요하다.

Additional Technical Detail

  • LLM의 Overhead는 100MB 이상이 될 수 있으므로 Console에서는 대용량 Memory Load를 지양하는 것이 좋다.
  • Test Setting에서의 LLM은 화면에 통계 페이지를 표시하지 않는다.
    • 하지만 .csv File에는 통계를 작성한다.
    • LLM은 Shipping에서 완전히 비활성화가 된다.
  • Asset Tag Tracking은 아직 초기 실험단계이다.

'UE5 > Utility' 카테고리의 다른 글

[Basic] Source Control  (0) 2024.05.09
[Basic] Packaging  (0) 2024.05.09
[ProgrammingTool] Sparse Class Data  (0) 2024.05.07

+ Recent posts