로우 레벨 메모리 트래커(Low-Level Memory Tracker, LLM) 는 언리얼 엔진(UE) 프로젝트에서 메모리 사용량을 트래킹하는 툴입니다. LLM은 범위 태그 시스템을 사용하여 언리얼 엔진과 OS에서 할당한 모든 메모리에 대한 계정을 유지합니다. LLM은 언리얼 엔진에서 사용하는 모든 플랫폼을 지원합니다.
LLM 트래커
현재 LLM에는 두 개의 트래커가 있습니다. 각 트래커에는 자체 할당 맵과 태그 스택이 있습니다. 디폴트 트래커(Default Tracker)는 엔진의 모든 할당용입니다. 둘 중 높은 레벨이며 FMemory
클래스 함수인 Malloc
을 통해 이루어진 할당을 기록합니다. stat LLM
및 stat LLMFULL
콘솔 명령에 대한 통계를 제공하는 트래커입니다. 플랫폼 트래커(Platform Tracker)는 로우 레벨 버전이며 OS에서 이루어진 모든 할당을 기록합니다. 예를 들어 Binned2
와 같은 함수에 의해 이루어진 내부 할당을 트래킹합니다. 따라서 디폴트 트래커 통계는 플랫폼 트래커 통계의 서브셋입니다.
LLM 구성
프로젝트에서 LLM을 활성화하려면 다음 명령줄 실행인자와 콘솔 명령을 사용하세요.
명령줄 실행인자 | 설명 |
---|---|
-LLM |
LLM을 활성화합니다. |
-LLMCSV |
모든 값을 CSV 파일에 지속적으로 기록합니다. -LLM을 자동으로 활성화합니다. |
-llmtagsets=Assets |
실험단계 기능입니다. 각 에셋별로 할당된 총계를 표시합니다. |
-llmtagsets=AssetClasses |
실험단계 기능입니다. 각 UObject 클래스 타입의 총계를 표시합니다. |
콘솔 명령 | 설명 |
---|---|
stat LLM |
LLM 요약을 표시합니다. 모든 로우 레벨 엔진 통계는 단일 엔진 통계 아래에 그룹화됩니다. |
stat LLMFULL |
모든 LLM 통계를 표시합니다. |
stat LLMPlatform |
OS에서 할당된 모든 메모리에 대한 통계를 표시합니다. |
stat LLMOverhead |
LLM에서 내부적으로 사용하는 메모리를 표시합니다. |
-LLMCSV
명령줄 실행인자를 사용하면 .CSV
파일이 saved/profiling/llm/
에 기록됩니다. 파일에는 현재 값(MB 단위)을 표시하는 각 태그에 대한 열이 포함됩니다. 기본적으로 5초마다 새 줄이 작성됩니다. 빈도는 LLM.LLMWriteInterval
콘솔 변수를 사용하여 변경할 수 있습니다.
LLM 태그
엔진(게임 코드 포함)에 의해 이루어진 모든 메모리 할당에는 속한 카테고리를 식별하는 태그 값이 할당됩니다. 즉, 모든 메모리는 한 번만 트래킹되고 누락되지 않으며 두 번 계산되지 않습니다. 모든 카테고리의 총계가 게임에 사용되는 총 메모리 양에 추가됩니다.
태그는 태그 범위 매크로를 사용하여 적용됩니다. 해당 범위 내에서 이루어진 모든 할당에는 지정된 태그가 제공됩니다. LLM은 태그 범위 스택을 유지 관리하고 할당에 최상위 태그를 적용합니다. LLM 통계는 stat LLM
또는 stat LLMFULL
콘솔 명령을 사용하여 게임 내에서 볼 수 있습니다. 각 태그의 현재 합계는 MB 단위로 표시됩니다. 또한 LLM은 값을 분석할 수 있도록 통계 값을 .CSV
파일에 기록합니다. 현재 엔진 내에 있는 태그 카테고리는 다음과 같습니다.
태그 이름 | 설명 |
---|---|
UObject | UObject 에서 상속된 모든 클래스와 프로퍼티를 포함하여 해당 클래스에 의해 시리얼라이즈된 모든 것이 포함됩니다. Uobject 는 다른 어떤 카테고리에서도 트래킹되지 않는 모든 엔진과 게임 메모리에 대한 포괄적 태그입니다. 이 통계에는 별도로 트래킹되는 메시 또는 애니메이션 데이터는 포함되지 않습니다. 레벨에 배치된 오브젝트의 수에 대응합니다. |
EngineMisc | 다른 카테고리에서 트래킹되지 않는 모든 로우 레벨 메모리입니다. |
TaskGraphTasksMisc | 자체 카테고리가 없는 태스크 그래프에서 시작되는 모든 태스크입니다. 일반적으로 상당히 낮아야 합니다. |
StaticMesh | UStaticMesh 클래스 및 관련 프로퍼티이며 실제 메시 데이터를 포함하지 않습니다. |
커스텀 태그
언리얼 인사이트로 프로젝트의 메모리 사용량을 프로파일링할 때 메모리 인사이트에서 LLM 비트래킹(LLM Untracked) 태그가 지정된 할당된 메모리가 있을 수 있습니다. LLM_DECLARE_TAG
및 LLM_DEFINE_TAG
매크로는 트래킹되지 않은 메모리 할당을 검색하는 데 도움이 되는 커스텀 태그를 만드는 데 사용됩니다. 이 매크로들은 엔진 파일을 수정할 필요가 없으며 게임 모듈이나 플러그인에서 수행할 수 있습니다. 생성한 커스텀 태그를 사용하려면 LLM_SCOPE_BYTAG
매크로를 사용하세요. 단계 요약은 다음과 같습니다.
LLM_DECLARE_TAG
를 사용하여 헤더 파일에 커스텀 LLM 태그를 선언합니다.- 관련
.cpp
파일에서LLM_DEFINE_TAG
를 사용하여 커스텀 LLM 태그를 정의합니다. LLM_SCOPE_BYTAG
를 사용하여.cpp
파일의 메모리 사용량을 트래킹하려는 경우LLM_SCOPE_BYTAG
를 사용합니다.
커스텀 태그의 선언 및 사용에 대한 간단한 예시는 아래의 예시를 참조하세요.
커스텀 태그 매크로
LLM_DECLARE_TAG
LLM_DECLARE_TAG
매크로는 LLM_SCOPE_BYTAG
에서 사용하거나 다른 LLM_SCOPE
에서 이름으로 참조할 수 있는 다른 곳에서 정의된 태그를 선언합니다.
- 파라미터
UniqueName
: 이름으로 조회하기 위한 태그의 이름입니다.LLM_DEFINE_TAG,
LLM_SCOPE
또는ELLMTag
에 전달된 모든 태그에 걸쳐 고유해야 합니다.
LLM_DEFINE_TAG
LLM_DEFINE_TAG
매크로는 LLM_SCOPE_BYTAG
에서 사용하거나 다른 LLM_SCOPE
에서 이름으로 참조할 수 있는 태그를 정의합니다.
- 파라미터
UniqueNameWithUnderscores
: 태그 이름의 수정된 버전입니다. 이름으로 조회하기 위해 사용합니다.LLM_DEFINE_TAG,
LLM_SCOPE
또는ELLMTag
에 전달된 모든 태그에 걸쳐 고유해야 합니다.LLM_DEFINE_TAG
에서 부모 항목에 대한 일반적인 구분자(/
)를 (_
)로 바꿔야 합니다.DisplayName
: (선택 사항) 부모 항목이 있는 경우 (/
)로 결합된 태그를 부모 항목의 이름으로 트래킹할 때 표시할 이름이며,UniqueName
을 사용하는 경우NAME_None
입니다.ParentTagName
: (선택 사항) 부모 태그의 고유 이름이며, 부모 태그가 없는 경우NAME_None
입니다.StatName
: (선택 사항) 매 프레임마다 LLM 데이터를 게시할 때 이 태그의 양으로 채울 통계의 이름이며, 통계를 채우지 않는 경우NAME_None
입니다.SummaryStatName
: (선택 사항) 매 프레임마다 LLM 데이터를 게시할 때 이 태그의 양에 추가할 통계 그룹의 이름이며, 통계 그룹을 추가하지 않는 경우NAME_None
입니다.
예시
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]);
}
태그 세트(실험단계)
태그 세트를 사용하려면 LLM_ALLOW_ASSETS_TAGS
in LowLevelMemTracker.h
를 정의합니다. 태그 세트를 사용할 때 각 할당은 에셋 이름 또는 오브젝트 클래스 이름을 추가로 저장합니다.
기술적 구현 세부 사항
LLM은 포인터로 인덱싱된 모든 할당의 맵을 유지함으로써 작동합니다. 맵에는 현재 각 할당의 크기와 할당된 태그가 포함됩니다. 게임에는 동시에 최대 400만 개의 라이브 할당이 있을 수 있으므로 메모리 오버헤드를 가능한 한 작게 유지하는 것이 중요합니다. 현재 구현에서는 할당마다 21바이트를 사용합니다.
할당 | 크기 |
---|---|
포인터 | 8바이트 |
포인터 해시 키 | 4바이트 |
크기 | 4바이트 |
태그 | 1바이트 |
해시 맵 인덱스 | 4바이트 |
OnLowLevelAlloc
함수를 사용하여 할당을 트래킹하면 태그 스택 맨 위에 있는 태그가 현재 태그가 되고, 해당 포인터를 키로 사용하여 할당 맵에 저장됩니다. 경합을 피하기 위해 각 태그의 프레임 델타는 별도의 FLLMThreadState
클래스 인스턴스에서 트래킹됩니다. 프레임이 끝나면 이러한 델타가 합산되어 통계 시스템과 .CSV
파일에 게시됩니다.
LLM은 초기에 초기화되므로 디폴트로 활성화해야 합니다. 명령줄에서 LLM이 활성화되어 있지 않으면 자동으로 종료되고 모든 메모리가 정리되므로 오버헤드가 발생하지 않습니다. LLM은 테스트(Test) 및 배포(Shipping) 빌드에서 완전히 컴파일됩니다.
LLM은 통계 시스템 없이 실행할 수 있습니다(예: 테스트 환경설정). 화면에 통계를 표시할 수는 없지만 통계는 여전히 .CSV
파일에 기록됩니다. LowLevelMemTracker.h
에서 ENABLE_LOW_LEVEL_MEM_TRACKER
를 수정하여 LLM을 활성화해야 합니다.
태그는 범위 매크로를 사용하여 적용됩니다. 두 가지 메인 매크로는 다음과 같습니다.
LLM_SCOPE(Tag)
LLM_PLATFORM_SCOPE(Tag)
이 매크로들은 각각 기본 트래커 및 플랫폼 트래커의 현재 범위를 설정합니다. 이러한 범위에는 플랫폼별 버전이 있습니다. 예를 들어 LLM_SCOPE_[콘솔](태그)
는 플랫폼별 태그 열거형 타입을 사용합니다. LLM_SCOPED_TAG_WITH_STAT
와 같이 통계를 사용하는 범위 매크로는 현재 지원 중단으로 간주되므로 사용해서는 안 됩니다.
LLM에서 내부적으로 사용하는 모든 메모리는 플랫폼에서 제공하는 LLMAlloc
및 LLMFree
함수에 의해 관리됩니다. LLM이 자체 메모리 사용량을 트래킹하지 않도록(그리고 무한 재귀를 유발하지 않도록) 다른 방식으로 할당하지 않는 것이 중요합니다.
추가 기술 세부 사항
다음 섹션에는 LLM을 사용할 때 알아야 할 다양한 참고 사항과 추가 정보가 있습니다.
- LLM의 오버헤드는 100MB 이상이 될 수 있으므로 콘솔에서는 대용량 메모리 모드로 실행하는 것이 좋습니다.
- 테스트 환경설정의 LLM은 화면에 통계 페이지를 표시하지 않지만
.CSV
파일을 작성합니다. LLM은 배포에서 완전히 비활성화됩니다. - 에셋 태그 트래킹은 아직 초기 실험단계입니다.