언리얼 엔진 5 (UE5)에서는 메모리 인사이트(Memory Insights) 기능에 개선된 메모리 트래킹 및 프로파일링 지원을 추가하여 언리얼 인사이트(Unreal Insights) 기능을 확장했습니다. 이제 개발자는 각 메모리 블록에 연결된 로우 레벨 메모리(Low Level Memory, LLM) 태그, 콜 스택 등 메모리 할당 및 할당 해제에 관한 자세한 정보를 언제든지 볼 수 있습니다. 메모리 인사이트는 원하는 시점의 라이브 할당을 찾고, 메모리 사용량의 증감을 파악하고, 단기/장기 할당을 구분하고, 메모리 누수를 찾을 수 있는 쿼리 시스템을 제공합니다.
세션 기록하기
다음 단계에 따라 메모리 인사이트를 사용하여 메모리 채널에 트레이스 기록을 시작할 수 있습니다.
언리얼 인사이트 실행 또는 빌드
시작(Start) > 명령 프롬프트(Command Prompt) 로 이동한 다음, 다음과 같이 입력합니다.
Engine\Binaries\Win64\UnrealInsights.exe
아니면, Engine\Binaries\Win64
폴더로 이동한 다음 UnrealInsights.exe
를 더블클릭하여 실행해도 됩니다.
메모리 트레이싱과 함께 게임 프로젝트 실행
운영 체제에서 명령 프롬프트 를 실행한 다음 프로젝트 샘플을 실행합니다.
cd C:\MyEngineInstallLocation\
Samples\Games\MyGameSample\Binaries\Win64\MyGameSample.exe -trace=default,memory
프로젝트 세션을 기록하려면 프로세스 처음부터 메모리 트레이스 채널을 활성화해야 합니다. 그렇지 않으면 이후 연결 세션에서 트레이싱 할당 이벤트를 시작할 수 없게 됩니다. 또한, 패키지로 만든 프로젝트에서 트레이스를 실행하는 경우, 개발(Development) 모드로 패키징되었는지 확인해야 합니다.
metadata
및 assetmetadata
트레이스 명령을 사용하여 에셋 이름과 클래스 이름을 추가적으로 필터링하는 옵션을 제공할 수 있습니다. 예를 들어, 에셋당 또는 클래스 이름당 메모리 할당 비용을 계산할 수 있습니다.
Samples\Games\MyGameSample\Binaries\Win64\MyGameSample.exe -trace=default,memory,metadata,assetmetadata
인사이트 세션 브라우저에서 트레이스 열기
다시 언리얼 인사이트 세션 브라우저(Unreal Insights Session Browser)로 이동한 다음, .utrace
파일을 더블클릭하여 분석을 위해 언리얼 엔진 타이밍 인사이트(Timing Insights) 창에서 엽니다. 메뉴(Menu) > 메모리 인사이트 를 선택하여 메모리 인사이트 창을 엽니다.
메모리 할당 - 그래프 트랙
언리얼 인사이트는 프로젝트의 할당 메모리를 분석한 데이터를 제공하기 위해 각 할당 이벤트의 완전한 콜 스택을 캡처합니다. 메모리 인사이트 의 메인 인터페이스는 세션에 사용되는 메모리의 개요를 표시하는 타임라인으로 구성됩니다.
메모리 인사이트 트래커는 메모리의 라이브 할당 횟수에 대한 정보를 표시합니다. 위 그림은 라이브 할당 및 해제 이벤트 수를 표시하는 메인 메모리 그래프입니다.
메인 메모리 그래프(Main Memory Graph) 는 프로젝트에서 트래킹한 메모리의 총량을 표시합니다(LLM 에서 수집한 각 태그 정보 등). 또한, 총 라이브 할당 횟수를 표시하는 그래프도 있습니다.
그래프 타입 |
색상 |
설명 |
---|---|---|
총 할당 메모리(Total Allocated Memory) |
파란색 |
자세한 할당 트래킹을 기반으로 각 시점에 할당된 총 메모리 양을 표시합니다. |
라이브 할당 횟수(Live Allocation Count) |
노란색 |
특정 시점의 총 활성 할당 수를 표시합니다. |
할당/해제 이벤트 수(Allocation/Free Event Count) |
녹색/주황색 |
"짧은 시간"으로 표시되는 유닛당 할당 및 해제 이벤트 수를 표시합니다. |
이러한 각 그래프는 자세한 할당 트래킹을 기반으로 합니다. 이러한 그래프는 시간 값 0에서 시작하며 약 1ms로 세분화됩니다. LLM 접두사 태그가 있는 다른 그래프(RenderTargets, SceneRender, UObject)는 로우 레벨 메모리 트래킹 런타임 시스템을 기반으로 합니다.
이러한 태그는 세션이 시작되고 몇 초 후에 트래킹을 시작하며 프레임당으로 세분화됩니다.
기본적으로 4096 할당/해제 이벤트당 하나의 타임스탬프를 내보냅니다. 필요하다면 Engine/Source/Runtime/Core/Private/ProfilingDebugging/MemoryAllocationTrace.cpp
에 있는 MarkerSamplePeriod를 수정하여 이 양을 변경할 수 있습니다. 예를 들어, 이 변수 값을 0으로 설정하면 각 할당/해제 이벤트 후에 타임스탬프를 내보냅니다.
메모리 인사이트 타임라인은 타이밍 뷰(Timing View) 에서 트랙 오버레이를 지원합니다. 메모리 인사이트 뷰를 사용할 때 타이밍(Timing), 조사(Investigation), LLM 태그(LLM Tags), 모듈(Modules) 이렇게 네 가지 패널을 사용할 수 있습니다.
타이밍 뷰
타이밍 을 클릭하여 타이밍 뷰 창의 디스플레이를 토글할 수 있습니다. 여기서 메모리 사용에 관련된 다양한 트랙의 퍼포먼스 데이터를 관찰하고 필터링할 수 있습니다.
조사 패널
조사 패널에서는 할당에 대해 다양한 쿼리를 할 수 있습니다.
로우 레벨 메모리(LLM) 태그
LLM 태그 패널에서는 다양한 LLM 태그의 비저빌리티를 제어합니다. 이 데이터는 운영 체제에서 바로 트레이싱됩니다.
LLM 태그, 에셋, 소스 파일 을 그룹으로 묶을 수 있습니다.
모듈
콜 스택 심볼이 리졸브되면 결과가 캐시 파일에 저장됩니다. 모듈 을 클릭하여 이러한 캐시 파일을 볼 수 있습니다. 이 패널에서는 예전 트레이스 파일을 열고 심볼을 사용할 수 있습니다.
열에서 발견됨(Discovered), 캐시됨(Cached), 리졸브됨(Resolved), 실패(Failed) 심볼 개수를 확인할 수 있습니다. 목록에서 실패 항목은 빨간색으로 강조되며 목록에서 제대로 리졸브된 아이템은 녹색으로 강조됩니다. 노란색은 일부 심볼은 리졸브되었지만 일부 심볼은 실패했다는 뜻입니다.
이전 메모리 트래킹의 런타임 구현은 Engine\Source\Runtime\Core\Public\HAL\LowLevelMemTracker.h
폴더에 있는 LowLevelMemTracker 클래스에서 구현됩니다. LLM 태그 패널과 LLM 그래프 모두 이 시스템에서 직접 트레이싱된 데이터를 사용합니다. 자세한 할당 데이터는 개별 특정 트레이스 구현에서 가져옵니다.
메모리 인사이트는 새 쿼리 기능과 트래킹된 메모리 할당 정보를 포함합니다. 특정 기간이나 특정 시점 전후로 UE5가 할당 및 해제한 메모리 블록을 식별할 수 있으며 메모리 누수 여부도 확인할 수 있습니다. 트레이스 로그를 열고 조사 탭으로 이동하여 쿼리 시스템에 액세스할 수 있습니다.
조사 - 할당 쿼리
타임라인이 메모리 사용에 대한 개요는 제공하지만, 일정 기간 개별 할당 동작 방식에 대한 평가는 "쿼리"를 사용하여 수행합니다. 쿼리(Query) 는 규칙(Rule) 과 A 라벨이나 B 라벨 등 하나 이상의 타임스탬프(Timestamps) 로 정의됩니다.
조사 패널 에는 데이터를 평가하는 쿼리 규칙이 있습니다.
사용 가능한 쿼리 규칙(Query Rules) 은 다음과 같습니다.
쿼리 규칙 |
시간 변수 |
설명 |
---|---|---|
활성 할당(Active Alloc) |
A |
시간 A의 모든 활성 할당을 표시합니다. |
전(Before) |
A |
시간 A 전의 모든 할당을 표시합니다. |
후(After) |
A |
시간 A 후의 모든 할당을 표시합니다. |
감소(Decline) |
A 및 B |
시간 A 전에 할당되고 시간 A와 B 사이에 해제된 모든 할당을 표시합니다. |
증가(Growth) |
A 및 B |
시간 A와 B 사이에 할당되고 시간 B 후에 해제된 모든 할당을 표시합니다. |
증가 대 감소(Growth Vs Decline) |
A 및 B |
"증가" 할당(시간 A와 B 사이에 할당되고 시간 B 후에 해제)과 "감소" 할당(시간 A 전에 할당되고 시간 A와 B 사이에 해제)을 모두 식별합니다. 감소 할당은 음수 크기로 변경되므로 크기 집계는 A와 B 사이의 베리에이션을 보여줍니다. 이 쿼리의 결과는 A 시간에 할당된 이벤트와 B 시간에 할당된 이벤트를 비교한 것입니다. 태그나 콜 스택별로 메모리 할당을 그룹으로 묶으면 각 그룹의 베리에이션(B~A)이 표시됩니다. |
해제 이벤트(Free Events) |
A 및 B |
시간 A와 B 사이에 해제된 모든 할당을 표시합니다. |
할당 이벤트(Alloc Events) |
A 및 B |
시간 A와 B 사이에 할당된 모든 할당을 표시합니다. |
단기 할당(Short Living Allocs) |
A 및 B |
시간 A 후에 할당되고 시간 B 전에 해제된 모든 할당을 표시합니다. 이 규칙은 스택 할당이 될 수 있는 할당, 즉 임시 또는 단기 라이브 할당을 식별하는 데 사용할 수 있습니다. |
장기 할당(Long Living Allocs) |
A 및 B |
시간 A 전에 할당되고 시간 B 후에 해제된 모든 할당을 표시합니다. |
메모리 누수(Memory Leaks) |
A, B, C |
시간 A와 B 사이에 할당되고 시간 C 후에 해제된 모든 할당을 표시합니다. 이는 레벨 전환 등 정해진 시간에 해제되어야 할 메모리를 찾는 데 유용합니다. |
제한된 수명(Limited Lifetime) |
A, B, C |
시간 A와 B 사이에 할당되고 시간 B와 C 사이에 해제된 모든 할당을 표시합니다. |
장기 수명 할당 감소(Decline of Long Living Allocs) |
A, B, C |
시간 A 전에 할당되고 시간 B와 C 사이에 해제된 모든 할당을 표시합니다. |
특정 수명(Specific Lifetime) |
A, B, C, D |
시간 A와 B 사이에 할당되고 시간 C와 D 사이에 해제된 모든 할당을 표시합니다. |
쿼리를 생성하려면 규칙을 선택하고 라벨이 달린 마커를 타임라인의 원하는 위치로 드래그하거나 조사 탭에서 시간을 지정합니다.
원하는 규칙과 시간을 선택하고 조사 탭에서 쿼리 실행(Run Query) 버튼을 클릭하여 쿼리를 생성합니다.
쿼리와 캡처되는 데이터 세트에 따라 쿼리를 실행하는 데 상당한 시간이 소요될 수 있습니다.
할당 분석 뷰
쿼리가 실행되면 새 창이 표시되며, 쿼리가 완료되면 할당 테이블이 결과로 채워집니다. 기본적으로 이러한 결과는 단순 목록으로 표시됩니다.
각 할당은 수(Count), 크기(Size), LLM 태그, 콜스택 크기(Callstack Size), 할당을 만든 함수(Function) 와 함께 표시됩니다.
함수 또는 정보 아이콘 위로 마우스를 가져가면 추가 상세 정보와 전체 콜 스택이 표시됩니다.
정보 아이콘 위로 마우스를 가져가면 전체 콜 스택이 표시됩니다.
정렬
테이블 헤더를 클릭하면 목록을 다양한 열을 기준으로 정렬할 수 있습니다.
열 정렬 |
설명 |
---|---|
할당 계층구조(Allocation Hierarchy) |
할당 트리의 계층구조를 기준으로 정렬합니다. |
할당 횟수(Allocation Count) |
할당 수를 기준으로 정렬합니다. |
크기 |
할당 크기를 기준으로 정렬합니다. |
LLM 태그 |
할당의 LLM 태그를 기준으로 정렬합니다. |
최상위 함수(콜스택)(Top Function (Callstack)) |
할당의 콜 스택에서 리졸브된 최상위 함수를 기준으로 정렬합니다. |
콜스택 크기(CallStack Size) |
콜스택 프레임 수입니다. |
그룹화
프리셋(Preset) 옵션을 사용하여 할당을 그룹화할 수 있습니다.
프리셋 옵션 |
설명 |
---|---|
디폴트(Default) |
디폴트 할당을 표시합니다. |
디테일(Detailed) |
디테일한 할당 정보를 표시하도록 트리 뷰를 구성합니다. |
힙(Heap) |
다양한 메모리 유형이 어떻게 사용되는지 조사합니다. 다수의 주소 공간을 참조하세요. |
크기(Size) |
대규모 할당을 빠르게 찾습니다. |
태그(Tags) |
시스템당 할당을 표시합니다. |
에셋(Asset) |
에셋 태그별로 할당을 분석하여 보여주는 트리 뷰를 구성합니다. |
클래스 이름(Class Name) |
클래스 이름별로 할당을 분석하여 보여주는 트리 뷰를 구성합니다. |
콜스택(Callstack) |
어느 콜 스택에서 할당이 오는지 조사합니다. |
반전된 콜스택(Inverted Callstack) |
반전된 콜 스택별로 할당을 분석하여 보여주는 트리 뷰를 구성합니다. |
주소(4K 페이지)(Address(4K Page)) |
주소를 기준으로 4K 정렬 메모리 페이지로 할당을 그룹화합니다.
|
계층구조(Hierarchy) 로 이동하여 모두(All) 를 클릭하면 기본 일반 뷰(Flat view) 를 추가적인 대안 그룹으로 변경하는 드롭다운 메뉴가 열립니다.
다음 옵션 목록에서 계층구조를 그룹으로 묶을 수 있습니다.
계층구조 그룹 |
설명 |
---|---|
플랫(Flat) |
모든 아이템을 포함하는 단일 그룹을 생성합니다. |
크기(Size) |
크기에 따라 할당을 그룹으로 묶습니다. |
태그(Tag) |
태그 계층구조에 따라 트리를 생성합니다. |
콜스택(Callstack) |
각 할당의 콜스택에 따라 트리를 생성합니다. |
반전된 콜스택(Callstack) |
각 할당의 콜스택에 따라 트리를 생성합니다. |
힙(Heap) |
힙에 따라 트리를 생성합니다. |
고유 값 - 이벤트 거리(Unique Values - Event Distance) |
각 이벤트 거리 값의 그룹을 생성합니다. |
고유 값 - 시작 시간(Unique Values - Start Time) |
각 시작 시간 값의 그룹을 생성합니다. |
고유 값 - 끝 시간(Unique Values - End Time) |
각 끝 시간 값의 그룹을 생성합니다. |
고유 값 - 기간(Unique Values - Duration) |
각 기간 값의 그룹을 생성합니다. |
고유 값 - 주소(Unique Values - Address) |
각 주소 값의 그룹을 생성합니다. |
고유 값 - 메모리 페이지(Unique Values - Memory Page) |
각 메모리 페이지 값의 그룹을 생성합니다. |
고유 값 - 크기(Unique Values - Size) |
각 크기 값의 그룹을 생성합니다. |
고유 값 - LLM 태그(Unique Values - LLM Tag) |
각 LLM 태그 값의 그룹을 생성합니다. |
고유 값 - 에셋(Unique Values - Asset) |
각 에셋 값의 그룹을 생성합니다. |
고유 값 - 클래스 이름(Unique Values - Class Nam) |
각 클래스 이름 값의 그룹을 생성합니다. |
고유 값 - 최상위 함수(Unique Values - Top Function) |
각 최상위 함수 값의 그룹을 생성합니다. |
고유 값 - 최상위 소스 파일(Unique Values - Top Source File) |
각 최상위 소스 파일 값의 그룹을 생성합니다. |
고유 값 - 콜스택 크기(Unique Values - Callstack Size) |
각 콜스택 크기 값의 그룹을 생성합니다. |
경로 분석 - LLM 태그(Path Breakdown - LLM Tag) |
LLM 태그 스트링 값의 구조에서 트리 계층구조를 생성합니다. |
경로 분석 - 에셋(Path Breakdown - Asset) |
에셋 태그 스트링 값의 구조에서 트리 계층구조를 생성합니다. |
경로 분석 - 최상위 소스 파일(Path Breakdown - Top Source File) |
최상위 소스 파일 스트링 값의 구조에서 트리 계층구조를 생성합니다. |
고급 필터링
텍스트 검색창을 사용하여 계층형 노드 텍스트를 기반으로 빠르게 결과를 필터링할 수 있습니다. 텍스트 검색창 옆에 있는 필터 컨피규레이터(Filter Configurator) 를 클릭하면 쿼리로 생성된 할당 세트를 추가로 필터링하여 할당 그룹으로 분리할 수 있습니다.
그룹과 AND/OR 키워드를 사용하여 고급 쿼리를 빌드할 수 있습니다.
콜 스택 심볼 리졸브
프로젝트의 콜 스택 심볼 트레이싱(Call Stack Symbol Tracing) 은 프로그램 카운터 주소를 사용하여 완료됩니다. 분석에서 이러한 주소는 해당 소스 파일 정보와 함께 읽을 수 있는 스트링으로 리졸브되어야 합니다.
그러기 위해서 메모리 인사이트는 디버그 정보를 포함하는 올바른 파일 버전(플랫폼에 따라 .pdb
또는 .elf
파일)에 액세스할 수 있어야 합니다. 인사이트는 다음 목록에 따라 올바른 파일을 검색합니다.
사용자가 이 세션에서 입력한 새 경로.
실행 파일의 경로(일부 플랫폼에서 사용할 수 있는 경우, 실행 파일은 바이너리로 컴파일됩니다).
UE_INSIGHTS_SYMBOLPATH
환경 변수의 경로. 이 변수는 세미콜론으로 구분된 경로를 허용합니다.사용자 환경설정 파일의 경로.
심볼이 리졸브되면 결과가 캐시 파일에 저장됩니다. 모듈 패널을 클릭하여 이러한 캐시 파일을 볼 수 있습니다. 그런 다음, 선택한 파일을 우클릭하고 드롭다운 메뉴에서 다음 옵션 중 하나를 선택하여 해당 파일을 열 수 있습니다.
이를 통해 다른 사용자가 해당 디버그 정보에 액세스할 필요 없이 트레이스 파일을 보낼 수 있습니다.
로드 방법 |
설명 |
---|---|
파일에서 심볼 로드(Load symbols from file) |
파일을 지정하여 모듈에 대한 심볼을 로드합니다. 성공하면, 같은 디렉터리에서 다른 실패한 모듈의 로드를 시도합니다. |
디렉터리에서 심볼 로드(Load symbols from directory) |
디렉터리를 지정하여 모듈에 대한 심볼을 로드합니다. 성공하면, 같은 디렉터리에서 다른 실패한 모듈의 로드를 시도합니다. |
심볼 리졸브는 현재 Win64, XB1/XSX, PS4/PS5 및 Switch에서 사용할 수 있습니다.
다수의 주소 공간
메모리 트레이싱은 여러 힙(heap) 에서 메모리를 트래킹합니다. 개념적으로 모든 할당은 반드시 루트 힙(root heap) 에 속하며 한 가지 메모리 유형을 나타내야 합니다. 예를 들어 데스크톱 플랫폼에서 루트 힙 중 하나는 시스템 메모리이며, 또 하나는 그래픽 카드의 비디오 메모리입니다. 각 루트 힙은 자체적인 주소 공간을 가집니다. 각 루트 힙 아래에는 할당을 호스트할 수 있는 힙 할당이 생성됩니다. 보통, 이는 할당을 백업하는 가상 메모리 할당을 뜻하지만, 블록-스타일 할당도 힙 할당으로 표현될 수 있습니다. 이를 통해 해당 메모리 블록의 사용을 조사할 수 있습니다.