언리얼 엔진에서 로우 레벨 메모리 트래커 사용하기 | 언리얼 엔진 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
- BindMalloc
LLM Configuration
Command-line Argument
- -LLM
- LLM 활성화
- -LLMCSV
- 모든 값을 csv 파일에 지속적으로 기록
- saved/profiling/llm/ 에 기록
- csv 파일에는 현재 값(MB)을 표시하는 각 Tag에 대한 Row가 포함된다.
- 기본적으로 5초마다 새 Row가 작성된다.
- Console 변수인 LLM.LLMWriteInterval을 사용하여 주기를 변경할 수 있다.
- -LLM이 자동으로 활성화
- 모든 값을 csv 파일에 지속적으로 기록
- -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 사용 방법
- LLM_DELCARE_TAG를 사용해 header File에 Custom LLM Tag를 선언
- 관련 .cpp File에서 LLM_DEFINE_TAG를 사용해 Custom LLM Tag를 정의
- 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_SCOPE
- LLM 내부에서 사용하는 모든 Memory는 Platform에서 제공하는 LLMAlloc/LLMFree 함수로 관리한다.
- 때문에 LLM이 자체 Memory 사용량을 Tracking 하지 않도록,
그리고 무한 Loop에 빠지지 않도록 다른 방식으로 할당하지 않는 것이 중요하다.
- 때문에 LLM이 자체 Memory 사용량을 Tracking 하지 않도록,
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 |