
1. 영상 정보
- 영상 제목: Optimizing the Game Thread | Unreal Fest 2024
- 채널명: Unreal Engine
- 영상 링크: https://youtu.be/KxREK-DYu70
2. 핵심 요약 (Conclusion)
- 프레임 버짓과 프로파일링 기초: 30 FPS(33.33ms) 및 60 FPS(16.66ms) 타겟 안에서 게임 스레드, 렌더 스레드, GPU가 연산 예산을 나누어 쓰는 개념을 정립하고, 이를 정밀 진단할 수 있는 언리얼 인사이트(Unreal Insights)의 핵심 인터페이스 및 데이터 추출 메커니즘을 상세히 다룹니다.
- 비동기 스레드 분산 및 하드웨어 최적화: 대량 연산을 분산하는 ParallelFor와 Task Graph 시스템을 소개하고, 콘솔(닌텐도 스위치, Xbox One) 환경의 성능 병목을 타파하기 위해 코어 개수 분석 및 캐시 미스를 방지하는 코어 어피니티(Core Affinity) 기법을 제시합니다.
- 틱(Tick) 오버헤드 타파와 구조적 개선: 함수 내 로직이 비어 있더라도 호출 자체로 프레임을 좀먹는 틱 태스크의 오버헤드를 진단하며, 불필요한 액터/컴포넌트/UMG 위젯의 틱 차단법, 어댑티브 틱 인터벌, 그리고 메모리 선형 로드를 통해 캐시 효율을 비약적으로 높이는 틱 애그리게이션(Tick Aggregation) 워크플로우를 증명합니다.
- 무거운 연산 제거 및 UI 레이아웃 경량화: 게임 프레임 내에서 발생하는 무브먼트 연산의 캐싱 및 지연 처리(Deferral) 방식을 제시하고, 무거운 UMG Canvas Panel을 Overlay/Grid로 대체해 비용을 50% 절감하는 방법, 시퀀서 기반 UI 애니메이션을 머티리얼(GPU 이관) 방식으로 전환하는 법, 제곱근 연산을 생략하는 Distance Squared 활용법을 다룹니다.
- 블루프린트 로직 및 노드 최적화 습관: 블루프린트 가상 머신의 함수 호출 비용을 진단하고 UE5의 '블루프린트 헤더 뷰' 파이프라인을 설명합니다. 특히 실행 핀 연결 개수만큼 매번 재실행되는 Pure(순수) 노드의 다중 평가 문제(지역 변수 캐싱으로 해결)와 조건과 관계없이 양쪽 식을 모두 계산하는 Select 노드의 병목 문제(Branch 노드로 교체해 해결)를 강력히 경고합니다.
- 네트워크 레이어의 CPU 부하 방어: 단일 코어로 구동되는 전용 서버(Dedicated Server)의 한계를 극복하기 위해, 매 프레임 속성을 순회 검사하는 비용을 제거하는 푸시 모델(Push Model), 원거리 액터 연산을 배제하는 네트워크 휴면(Network Dormancy), 구조체 배열 압축 전송을 지원하는 패스트 어레이 직렬화(Fast Array Serializer) 및 양자화(Quantization) 등의 CPU 경량화 기법을 제안합니다.
3. 타임라인별 상세 정리
1. 프로파일링 기초 및 언리얼 인사이트 분석 (00:00:01 ~ 00:05:35)
- 세션 개요 및 연사 소개 (00:00:01): Tanglewood Games의 어소시에이트 리드 프로그래머 Jake Simpson이 발표를 진행하며, PS4, Xbox One, Nintendo Switch 등 8세대 콘솔 플랫폼에서 'Quidditch Champions'를 최적화하며 정립한 실무 지식을 기반으로 게임 스레드 프로파일링 기법을 공유합니다. [00:27]
- 타겟 프레임 타임 예산 (00:02:21): 성능 최적화의 첫걸음은 프레임 타임 버짓을 명확히 아는 것입니다. 30 FPS 달성을 위해서는 프레임당 총 33.33ms, 60 FPS 달성을 위해서는 16.66ms의 시간 제한이 주어지며, 이 예산은 게임 스레드뿐만 아니라 렌더 스레드, RHI, GPU가 유기적으로 나누어 사용해야 합니다. [02:26]
- 언리얼 인사이트(Unreal Insights) 인터페이스 구조 (00:03:04):
- 추적 데이터 수집: 명령줄이나 콘솔에서 -Trace= 뒤에 원하는 기본 채널들을 명시하여 추적 캡처(Trace)를 생성할 수 있습니다. [03:25]
- 프레임 윈도우(Frame Window): 상단 탭 아래에서 프레임별 경과 시간을 타임라인 그래프로 시각화해 보여주며, 타겟 버짓 초과 여부에 따라 색상이 다르게 표현되어 히치(Hitch, 간헐적 끊김 현상)가 발생한 특정 프레임을 매우 직관적으로 찾아낼 수 있습니다. [03:53]
- 타이밍 윈도우(Timing Window): 메인 영역으로 GPU, Game Thread, RHI 등 다양한 스레드의 호출 스택(Call Stack) 구조를 타임라인 순서대로 세밀하게 분석할 수 있습니다. 한 단계 더 나아가 타임라인 가시성을 극대화하려면 콘솔에 stat namedevents 명령을 활성화하여 엔진의 커스텀 이벤트 이름까지 명확히 표기되도록 유도해야 합니다. [04:19]
- 타이밍 및 카운터 창(Timings & Counters): 우측 영역에서 사용자가 타임라인상에 지정한 드래그 범위 내 모든 이벤트의 통계를 산출합니다. 이벤트의 호출 횟수(Count), 하위 호출 함수 시간을 모두 더한 포함 시간(Inclusive Time), 하위 함수들을 제외하고 오직 해당 이벤트 노드 자체에서 소모한 순수 시간(Exclusive Time)을 제공하므로 평균 소모 시간이 가장 높은 진짜 병목 지점을 발굴하는 기준이 됩니다. [05:00]
2. 게임 스레드의 역할 및 비동기 작업 최적화 (00:05:36 ~ 00:12:00)
- 게임 스레드 부하 분산 전략 (00:05:36): 게임 스레드는 게임 전반의 메인 로직과 상태 업데이트가 수행되는 핵심 공간입니다. 이 스레드의 점유율을 줄이기 위해 백그라운드 스레드로 연산을 넘기는 비동기(Asynchronous) 기법이 활용됩니다. [05:38]
- 하드웨어 한계 및 코어 어피니티(Core Affinity) 제어 (00:06:34):
- 닌텐도 스위치는 사용 가능한 CPU 코어가 3개에 불과해 게임, 렌더, RHI, 오디오, 백그라운드 스레드를 배치하면 코어가 쉽게 고갈됩니다. [06:41]
- Xbox One 같은 하드웨어는 코어 간에 스레드를 옮겨 다니며 실행하는 컨텍스트 스위칭 비용이 매우 묵직합니다. 이를 방어하기 위해 게임 스레드가 오직 0번 코어(Core 0)에서만 고정 실행되도록 묶어두는 코어 어피니티(Core Affinity) 설정을 적용해야 하며, 이를 통해 고비용의 캐시 미스(Cache Miss)와 캐시 리로드 현상을 전면 차단해야 합니다. [06:59]
- 표준 프레임 시퀀스의 타임라인 흐름 (00:07:45): 전형적인 게임 프레임은 입력(Input) 데이터 수집으로 문을 열고, 곧바로 무브먼트(Movement) 및 충돌(Collision) 연산 단계로 이어집니다. 무브먼트의 경우 루트 액터가 움직이면 연쇄적으로 내부의 수많은 자식 컴포넌트와 부착 액터들이 줄줄이 업데이트되므로 타이밍 그래프상에서 연속된 계층 형태로 돋보이게 길게 나타나는 특성이 있습니다. [08:02]
- 비동기 동기화 대기 병목(WaitForTask) 진단법 (00:08:51): 애니메이션(Animation), 나이아가라(Niagara) 이펙트, 피직스(Physics)는 대개 '게임 스레드에서 연산 큐 실행 -> 비동기 스레드에서 무거운 연산 처리 -> 다시 게임 스레드로 복귀하여 완료 처리'라는 프레임워크 구조를 공유합니다. [08:58]
- 만약 타이밍 창에서 게임 스레드가 아무것도 안 하고 멈춰 있는 WaitForTask 이벤트 구간이 길게 잡힌다면, 이는 게임 스레드의 코드가 느린 게 아니라 분산시켜 놓은 나이아가라나 피직스 비동기 태스크의 연산량이 너무 많아 게임 스레드가 다음 단계로 가기 위해 마냥 기다리고 있는 상황을 대변합니다. [10:15]
- 이를 교정하기 위해서는 대기를 유발하는 특정 나이아가라 이펙트 등을 추적해 프레임의 훨씬 앞쪽 타이밍으로 실행 큐를 전진 배치하거나, 시스템 자체의 파티클 개수 등을 다듬어 비동기 스레드 완료 타이밍을 당겨야 합니다. [11:40]
3. C++ 프로파일링 매크로 및 데이터 분석 규칙 (00:12:01 ~ 00:15:15)
- 커스텀 코드 계측 매크로의 종류 (00:12:01): 프로젝트 고유의 소스 코드가 소모하는 시간을 언리얼 인사이트에 정확히 띄우려면 코드 블록마다 적합한 프로파일링 매크로를 명시해야 합니다. [12:14]
- 프로파일링 오버헤드(Profiling Overhead) 경고 (00:13:08): 이러한 계측용 매크로 시스템 역시 실행 시 CPU 자원을 미세하게 점유하므로, 대량 반복문 내부 등에 무분별하게 심어두면 실제 연산 로직보다 프로파일링 시스템 자체의 부하로 인해 시간이 뻥튀기되어 보이는 부작용을 낳습니다. [13:13]
- 신뢰도 높은 데이터 수집을 위한 가이드라인 (00:13:32):
- 다양한 플레이스루(Playthrough) 확보: 한두 프레임의 단편적인 결과나 단 한 번의 플레이 패턴만 보면 잘못된 결정을 내리기 쉽습니다. 플레이어의 불규칙한 조작과 변수들을 고루 반영한 긴 데이터 세트를 정밀 분석해야 합니다. [13:40]
- 멀티 플랫폼 크로스 체크: PC 스펙과 거실용 콘솔 기기는 하드웨어 병목 구간이 완전히 상이하므로 타겟 기기별로 교차 측정을 완수해야 합니다. [13:50]
- 에디터 제거 및 패키지 빌드 필수: 언리얼 에디터(Editor) 환경은 개발 편의용 백그라운드 오버헤드가 엄청나므로 최적화 수치 획득용으로는 전적으로 부적합합니다. 반드시 독립된 패키지 빌드로 추출하되, 디버깅 코드가 깨끗이 날아가 상용 환경에 가장 근접한 수치를 보장하는 Test 구성(Configuration)으로 빌드하여 관찰해야 합니다. [14:20]
- 결정론적 자동화 테스트 시스템: 테스트를 돌릴 때마다 결과가 요동치면 수정 전후의 성능 격차를 증명할 수 없습니다. 게임 플레이를 자동화 매크로나 스크립트로 구축해 항상 똑같은 동선과 조건 속에서 성능 수치를 획득하는 deterministic(결정론적) 환경을 구축하는 것이 권장됩니다. [14:48]
4. 액터 및 컴포넌트 틱(Tick) 최적화와 대량 처리 (00:15:16 ~ 00:25:40)
- 틱(Tick)의 아키텍처와 잠재 비용 (00:15:16): 언리얼의 틱 시스템은 매 프레임 업데이트를 수행하기 위해 내부적으로 FTickFunction 구조체를 가동하며, 각각 액터(PrimaryActorTick)와 컴포넌트(PrimaryComponentTick) 레벨로 쪼개져 관리됩니다. 하나의 객체에 여러 목적의 커스텀 틱을 다중 등록해 돌리는 것도 기술적으로 가능합니다. [15:25]
- 틱 그룹(Tick Groups) 단계별 실행 구조 (00:16:01): 틱은 프레임 타임라인 안에서 Pre Physics -> Start Physics -> During Physics -> End Physics -> Post Physics 구조로 단계별 실행을 이어갑니다. 스켈레탈 메시나 나이아가라 같은 주요 컴포넌트들은 피직스 최종 연산 데이터와 유기적으로 엮여 완료되어야 하므로 End Physics 시점에 완결을 보기 위해 멈추는 동기화 병목이 잦습니다. [16:06]
- 미사용 틱의 완벽한 비활성화 (00:17:51): 인사이트 창에서 로직 내용물이 거의 없는 수많은 마이크로 초 단위의 미세한 이벤트 선들이 빼곡하게 쌓여 프레임을 갉아먹는 현상을 발견했다면, 이는 틱 함수 내부의 연산이 무겁다기보다는 태스크 매니저가 틱을 실행하기 위해 호출하는 ExecuteTask 프로세스 자체의 관리비가 누적된 결과입니다. [17:57]
- UMG 위젯 블루프린트 및 C++ 베이스 위젯의 틱 버그 방지 (00:18:56): UI 위젯의 틱은 'Never'와 'Auto' 모드로 갈립니다. Auto 모드는 슬레이트 애니메이션이나 레이턴트 액션이 감지될 때만 스마트하게 돌아가지만, 디자이너나 작업자가 위젯 블루프린트 이벤트 그래프에 'Event Tick' 노드를 단지 생성해 배치만 해두어도(내부에 선을 아무것도 잇지 않은 빈 노드일지라도) 엔진은 매 프레임 강제 무조건 틱을 구동시키는 심각한 낭비를 초래합니다. [19:05]
- 또한, C++ 위젯 클래스를 부모로 상속받아 유저 위젯 블루프린트를 만드는 구조라면, C++ 클래스 상단 메타데이터 표기에 DisableNativeTick을 빼먹을 시 엔진 단에서 C++ 코드가 틱을 혹시 구현했는지 지속 검사하기 위해 매번 빈 네이티브 틱을 호출하는 버그성 비용이 들어갑니다. 이는 클래스 상속 계층의 모든 레이어마다 개별적으로 꼼꼼히 명시해야 제거됩니다. [19:38]
- 어댑티브 틱 인터벌(Adaptive Tick Interval) 알고리즘 (00:20:22): 로직상 틱을 무조건 유지해야만 하는 특수 객체라면 TickInterval 파라미터를 사용해 빈도를 조절합니다. 예를 들어 초당 1번만 갱신해도 되는 정보는 값을 1.0으로 수정합니다. 한 걸음 더 나아가 플레이어가 특정 트리거 행위를 취할 때만 일시적으로 틱 인터벌을 촘촘하게 좁히고, 평소 대기 상황에는 수 프레임 단위로 인터벌을 동적으로 길게 늘려버리는 적응형(Adaptive) 시스템으로 코딩할 수 있습니다. 단, 이 기법은 반응 속도에 일정 수준의 프레임 지연(Latency)이 동반되므로 신중히 검토하여 적용해야 합니다. [20:42]
- 내장 프레임워크 최적화 플러그인 기법 (00:22:23):
- 시그니피컨스 매니저(Significance Manager): 화면상에 나타나는 액터의 실제 픽셀 크기, 카메라로부터의 거리, 시야각 내 가시성 여부를 정밀 분석하여 각 액터의 틱 빈도와 상세 디테일 옵션을 실시간 등급별로 낮추거나 높여주는 완성형 최적화 인프라 프레임워크입니다. [22:23]
- 애니메이션 버짓 할당자(Animation Budget Allocator): 게임 스레드가 애니메이션 처리에 쓸 수 있는 총 밀리초 시간 한도를 딱 고정해 두고, 카메라에서 멀어진 주변부 소외 캐릭터들의 애니메이션 프레임레이트를 자동으로 떨어뜨리거나 프레임 간 보간 처리로 때워 메인 스레드 폭발을 원천 봉쇄하는 솔루션입니다. [22:50]
- 캐시 미스를 깨부수는 틱 애그리게이션(Tick Aggregation) 구조 전환 (00:23:23): 동일한 유형의 데이터 객체나 소형 액터 수십, 수백 개가 각자 자기 주도적으로 틱 태스크를 호출하면, 난잡하게 흩어진 메모리 주소에 무작위로 접근하게 되어 끔찍한 CPU 캐시 미스가 발생하고 태스크 스케줄링 관리 부하가 극에 달합니다. [23:23]
- 이를 혁신하기 위해 개별 객체들의 모든 독립 틱을 완전히 전멸시켜 버리고, 단 하나의 거대한 '마스터 매니저 틱 함수(One Singular Tick Function)'만 구동시킵니다. 매니저가 메모리상에 조밀하고 예쁘게 정렬된 객체 데이터 포인터 배열을 들고 매 프레임 단일 루프를 순차적으로 돌며 수동 업데이트 함수를 연이어 때리는 구조로 통합합니다. [24:09]
- 이렇게 구조를 일원화하면 CPU 하드웨어 단에서 메모리 데이터를 선형적으로 미리 예측하여 완벽하게 캐싱(Linear Load)하므로 속도가 경이롭게 상승합니다. (Tanglewood Games의 실무 테스트 기준, 18개 객체의 개별 틱 구조를 통합 매니저 구조로 바꾼 것만으로 2.8ms의 무거운 부하를 단 0.95ms로 낮추어 약 1.85ms의 유의미한 예산을 완전히 세이브했습니다.) [25:08]
5. 연산 결합, 무브먼트 지연 및 UI 렌더링 부하 절감 (00:25:41 ~ 00:29:45)
- 무브먼트 연산의 캐싱 및 지연 처리(Deferral) (00:25:41): 특히 페이스가 빠른 대형 멀티플레이어 슈팅 게임 등에서는 서버에서 내려온 좌표 보정, 클라이언트 측 보간 루틴, 그리고 로컬 단의 다음 프레임 예측 연산이 한 프레임 내부에서 뒤엉켜 동일한 액터의 Movement 처리와 이에 종속된 수많은 하위 컴포넌트 위치 재계산이 2~3회 이상 중복으로 폭발하는 비효율이 발생합니다. [26:04]
- 좌표 수정 명령이 내릴 때마다 즉각 트랜스폼을 동기화하지 말고, 임시 캐싱 변수에 최종 가야 할 델타 값만 실시간으로 합산(Caching)해 모아둔 다음, 프레임이 마감되기 직전 가장 알맞은 단 하나의 타이밍에 단 한 번만 최종 트랜스폼을 적용하는 지연 처리(Deferral) 기법을 활용해 중복 이동 연산을 하나로 병합해야 합니다. [26:22]
- UMG 레이아웃 비용 경량화 (Canvas Panel 전면 퇴출) (00:27:02): UI 디자인 시 무심코 기본 배치하는 캔버스 패널(Canvas Panel)은 매우 무거운 컴포넌트입니다. 화면 해상도 대응을 위한 상하좌우 앵커 정렬, 유연한 사이즈 계산, Z-Order 레이어 정렬 등 만능 기능을 한 몸에 우겨넣은 구조이기 때문입니다. [27:02]
- 고정된 HUD나 단순 배치 구조물 형태의 정적 UI라면 캔버스 패널을 가차 없이 걷어내고, 가볍고 순수한 오버레이(Overlay) 패널이나 그리드 패널(Grid Panel) 구조로 재조립하는 것만으로 UMG 슬레이트 레이아웃 드로우 연산 비용을 즉각 약 50% 절감하는 막강한 최적화 효율을 냅니다. [27:22]
- UI 애니메이션 파이프라인의 설계 교정 (00:27:43): UMG의 타임라인 기반 슬레이트 애니메이션 시스템은 내부적으로 무거운 언리얼 시퀀서(Sequencer) 모듈을 UI 스페이스 내부로 고스란히 끌어들여 구동하므로 ECS(엔티티 컴포넌트 시스템) 계층 오버헤드를 일으켜 게임 스레드를 압박합니다. [27:45]
- 이를 대체하기 위해 타이머 노드 기반의 코드로 경량화하거나, 가장 추천되는 고성능 기법으로는 애니메이션 연산 구조를 UI 전용 머티리얼(Material) 내부의 수학적 파라미터 연산으로 정밀하게 구현하는 것입니다. 이를 통해 UI 애니메이션 연산 비용을 메인 게임 스레드에서 완전히 도려내어 한가한 GPU 인프라 쪽으로 전량 강제 이관시킬 수 있습니다. [28:10]
- 수학 공식의 비용 절감 (Distance Squared 활용) (00:28:23): 몬스터 인공지능이나 탐색 알고리즘에서 거리를 기반으로 적을 색출하거나 순위를 매길 때 단순 거리 구하기 노드를 사용하면, 수식 내부에서 피타고라스 정리의 최종 단계인 고비용의 제곱근(sqrt) 함수를 연이어 타게 됩니다. [28:23]
- 대량 루프 검사 시에는 제곱근 연산을 통째로 건너뛰고 비교 기준이 되는 사정거리 값 자체를 미리 제곱 값으로 확보해 둔 뒤, 연산 장치에 부담이 없는 Distance Squared 함수를 통해 제곱 대 제곱 값 상태로 크기를 비교 제어하면 수학적 무결성은 완벽히 유지하면서 CPU 연산 사이클을 대폭 아낄 수 있습니다. [28:36]
6. 블루프린트 오버헤드 요인과 논리 노드 최적화 (00:29:46 ~ 00:34:55)
- 블루프린트 성능 저하의 본질 (00:29:46): 블루프린트가 성능을 잡아먹는 진짜 주범은 연산 로직 자체의 한계라기보다는 가상 머신(VM) 레이어에서 수많은 사각형 시각 노드 선을 타고 넘어갈 때 발생하는 '노드 간 함수 호출 오버헤드(Function Call Overhead)'의 누적량입니다. 성능 점검 시에는 인사이트 통계 창에서 BlueprintTime 스태츠를 명확히 정렬하여 누적 부하를 체크해야 합니다. [29:57]
- 네이티비제이션(Nativization) 파이프라인의 최신 동향 (00:30:48): UE4 시절에 제공되던 자동 블루프린트-C++ 변환 소스코드 요리 기법(Automatic Nativization)은 요리 컴파일(Cook) 타임에만 기계가 임의 가동되어 사람이 코드를 전혀 알아볼 수 없었고, 크래시 디버깅이 불가능해 UE5 가문에서 영구 폐기되었습니다. [31:05]
- UE5 진영에서는 '블루프린트 헤더 뷰(Blueprint Header View)' 툴링을 새로이 제공합니다. 이 도구는 블루프린트 구조체와 선언된 함수 시그니처 형체에 맞춰 깨끗한 네이티브 C++ 헤더 파일(.h) 형식을 자동으로 깔끔하게 생성해 주며, 실제 알맹이가 되는 함수 본문(.cpp) 코드 연산 로직은 프로그래머가 직접 눈으로 보며 수동으로 안전하게 포팅 및 이관하도록 가이드하는 정석적이고 안전한 수동 마이그레이션 워크플로우로 정착되었습니다. [31:27]
- Pure(순수) 노드의 연쇄 다중 평가 병목 경고 (00:32:24): 하얀색 실행 입력/출력 핀이 존재하지 않고 오직 데이터 반환 값 핀만 삐죽하게 튀어나와 있는 Pure 함수 노드는 코딩의 가치관을 오염시키는 가장 위험한 주범 중 하나입니다. Pure 노드는 결과가 한 번 나왔다고 값을 기억해 주는 장치가 전혀 없으며, 출력 핀에서 뻗어나간 선의 개수만큼 매 프레임 함수 전체를 완전히 처음부터 독립적으로 재실행하는 치명적인 구조로 작동합니다. [32:24]
- 가령, 무거운 계산을 수행하는 Pure 노드의 출력 선을 복사하여 하위의 서로 다른 연산 기능 노드 3곳의 입력으로 연결해 두었다면, 똑같은 결과 데이터를 받아오기 위해 백그라운드에서는 무거운 연산 함수가 3번 연속 고스란히 중복 구동됩니다. 이는 Out 레퍼런스 참조 파라미터나 구조체를 쪼개는 Break 구조체 노드 배치에서도 동일하게 중복 평가의 늪을 형성합니다. [32:33]
- 올바른 최적화 (로컬 변수 캐싱 습관): 연산 루틴 초입에서 단 한 번만 일반 실행 핀이 달린 함수 형태로 값을 명확히 호출한 뒤, 추출된 결과 데이터를 지역 변수(Local Variable)에 깨끗하게 임시 저장을 완료(Caching)해 두고, 하위 배치 구조에서는 오직 그 지역 변수 노드만 복사해 사용하도록 소스코드를 원천 개조해야 이 오버헤드가 박멸됩니다. [33:40]
- For/ForEach 루프 내부의 Pure 배열 바인딩 금지 (00:33:13): 루프 노드의 대상이 되는 Array 입력 타겟 핀 자리에 '특정 컴포넌트들의 배열 무리를 수집해 리턴하는 Pure 함수 노드'를 다이렉트로 꽂아버리면, 루프를 돌아야 할 엘리먼트 배열 개수가 100개일 때 매 프레임 컴포넌트를 수집하는 무거운 Pure 연산이 내부적으로 무려 100번 연속 중복 가동됩니다. 반드시 루프 가동 전에 로컬 변수로 배열을 미리 온전히 받아두고 배정해야 합니다. [33:13]
- Select 노드의 입력 후보 사전 양방향 평가 병목 (00:34:03): 조건 값 분기에 따라 알맞은 형체의 입력 라인을 선택해 패스해 주는 Select 시리즈 노드(Select Int, Select Float, 와일드카드 포함)는 코드 모양을 이쁘게 빚어주지만 최적화 측면에서는 거대한 지뢰밭입니다. 언리얼 엔진의 가상 머신은 Select 노드를 만나면 조건의 참/거짓 판단을 수행하기도 전에, 위아래 연결되어 대기 중인 모든 입력 후보 노드 체인의 연산 식을 상시 100% 무조건 사전 평가(Evaluate)해 버리기 때문입니다. [34:03]
- 평소 95%의 압도적인 확률로 가벼운 로컬 캐시 상수를 선택해 가고 아주 가끔 5%의 확률로만 돌아가는 복잡하고 무거운 수학 공식 계산 식을 아래 칸에 연결해 두었더라도, 실시간 엔진 백그라운드에서는 5%짜리 무거운 수학 공식 연산 식을 조건 상관없이 매 프레임 100% 상시 가동시키는 어처구니없는 연산 낭비 병목이 누수됩니다. [34:15]
- 올바른 최적화 (Branch 구조 복귀): 이 경우 Select 노드를 완벽히 제거하고 정석적인 분기(Branch) 노드 체계로 되돌려야 합니다. Branch 구조로 변경해야만 실행 핀 흐름 자체가 한쪽 루트로 단절(Short-Circuiting Execution)되므로, 원치 않는 반대편 노드의 계산 식 연산 처리가 완벽하게 차단되어 필요한 순간에만 비용이 올바르게 지출됩니다. [34:32]
7. 네트워크 오버헤드 완화 및 서버 CPU 부하 방어 (00:34:56 ~ 00:37:53)
- 전용 서버(Dedicated Server) 하드웨어의 병목 특성 (00:34:56): 멀티플레이 환경을 책임지는 전용 서버는 모니터 화면 출력 및 렌더 스레드 연산이 완전히 생략된 텍스트 기반 연산 머신입니다. 클라우드 인프라나 서버 팜 비용 절감 및 대량 멀티 인스턴스 동시 구동을 확보하기 위해 태생적으로 단일 코어(Single Core) 환경 위에서 독자 동작하도록 프로그래밍됩니다. 따라서 렌더 스레드로 도망칠 곳이 없어 순수 CPU 연산 성능 방어가 서비스 안정성의 전부를 좌우합니다. [35:08]
- 네트워크 레이어가 CPU를 잠식하는 핵심 원인 (00:35:34):
- 서버 CPU는 리플리케이션(Replication) 마킹이 박힌 메모리상의 수많은 데이터 변수 속성들의 값이 지난 프레임 대비 조금이라도 변했는지 매 프레임 무식하게 전체 순회 검사(Tick Check)해야 하는 막대한 오버헤드가 있습니다. [35:34]
- 다수의 클라이언트에 밀어내는 멀티캐스트 및 클라이언트 타겟 rpc 전송 직전 데이터를 전송 규격으로 압축 직렬화하는 패킹(Packing) 가동 오버헤드와 클라이언트들이 역으로 밀어 올리는 서버 RPC 요청 데이터 덩어리를 뜯어 분석하는 언패킹(Unpacking) 부하가 CPU 코어를 지속적으로 무겁게 짓누릅니다. [35:40]
- 네트워크 레이어 CPU 경량화 4대 핵심 솔루션 (00:36:03):
- 푸시 모델(Push Model) 전환: 엔진이 수천 개의 속성 변수들을 매 순간 무식하게 순회 감시하며 시계를 낭비하는 올드 워크플로우를 완벽히 폐기합니다. 소스 코드 소유주가 C++ 단에서 명시적으로 값이 진짜 변했을 때만 해당 속성 값을 향해 '더티(Dirty)' 상태 마크를 콕 집어 찍어주게 강제 유도함으로써 서버가 순회 검사하는 CPU 오버헤드를 0에 가깝게 소멸시킵니다. [36:03]
- 네트워크 휴면(Network Dormancy) 선언: 맵 반대편 저 멀리 털어져 있거나 당장 상태 변화를 실시간으로 추적해 패킷을 쏘아줄 필요가 없는 정적 액터 오브젝트들의 상태를 즉시 휴면(Dormant) 상태로 전환하여 서버 리플리케이션 체크 라인업 리스트에서 영구 제외해 버립니다. 이후 플레이어가 반경 내부로 근접 조정해 들어올 때만 알람을 주어 잠깨우는 수동 기법을 도입해야 합니다. [36:22]
- 패스트 어레이 직렬화(Fast Array Serializer) 구현: 언리얼의 표준 구조체 배열(TArray) 형태 데이터를 네트워크 복제할 때, 배열 전체를 통째로 직렬화하여 대역폭과 CPU를 낭비하지 않고, 내부적으로 오직 인덱스 변화를 정밀 파악해 정확히 추가/삭제/수정 변조가 가해진 특정 엘리먼트 데이터 알갱이만 미세 직렬화하여 쏘아내는 정밀 가속 장치입니다. [36:41]
- 데이터 양자화(Quantization) 압축 기법: 3D 좌표를 구성하는 FVector 등의 거대한 부동 소수점(Float) 정밀도 정밀 연산 영역을 가차 없이 깎아냅니다. (예: 소수점 셋째 자리까지 엄밀히 전송하던 규칙을 버리고, 패킹 매크로를 입혀 소수점 첫째 자리까지만 압축해 강제 압축 전송) 패킷의 바이트 단위를 가볍게 다이어트시키는 것은 물론, 서버와 클라이언트가 실시간 수행하는 패킹/언패킹 연산 단계의 CPU 사이클 소모량을 대폭 축소하는 데 탁월한 효과를 발휘합니다. [37:00]
'이펙트 (FX) > 이펙트 팁 : Unreal' 카테고리의 다른 글
| [Unreal/Optimizing] Optimizing Niagara for Performant Worlds | Unreal Fest Orlando 2025 요약 (0) | 2026.07.08 |
|---|---|
| [Unreal/Material] Custom Primitive Data 소개 (0) | 2026.07.08 |
| [Unreal/Niagara] 나이아가라 모듈 스크립트에서 원하는대로 분기 처리하기 (NMS, Enum, Static Switch) (0) | 2026.06.19 |
| [Unreal/Niagara] 나이아가라 모듈 스크립트 GPU Fixed Bounds 설정법 (0) | 2026.06.12 |
| [Unreal/Niagara] 나이아가라 네임스페이스 정리 (0) | 2026.06.10 |
댓글