PART 2 · 강의 4/4

캐시 친화적 데이터 구조

Data Oriented Design으로 CPU 캐시 효율을 극대화합니다

01

Data Oriented Design (DOD)

왜 데이터 배치가 중요한가

Data Oriented Design은 코드 병렬화, SIMD 연산 벡터화, 캐시 효율성을 통해 상당한 성능 향상을 가져옵니다.

CPU 캐시의 중요성

CPU가 메모리에서 데이터를 가져올 때, 필요한 데이터 주변도 함께 캐시에 로드합니다. 연속된 메모리에 관련 데이터가 있으면 캐시 히트율이 높아집니다.

메모리 접근 속도 비교

⚡ CPU 캐시 계층별 지연 시간
L1 Cache:
~1ns ⭐
L2 Cache:
~4ns
L3 Cache:
~12ns
Main Memory:
~100ns ⚠️

Main Memory는 L1 Cache보다 100배 느림!

02

AoS vs SoA

데이터 레이아웃 비교

📦 데이터 레이아웃 비교
AoS (Array of Structures)
[R0: P V H S N][R1: P V H S N]...

Position만 필요해도
전체 구조체 로드
캐시 낭비!

SoA (Structure of Arrays)
[P0 P1 P2...][V0 V1 V2...]...

필요한 데이터만
연속 메모리 접근
캐시 히트!

Array of Structures (AoS) - 전통적 방식

❌ AoS - 캐시 비효율적
코드 보기
// 전통적인 객체 지향 방식 struct Robot { FVector Position; // 12 bytes FVector Velocity; // 12 bytes float Health; // 4 bytes float Shield; // 4 bytes FString Name; // 많은 bytes }; TArray<Robot> Robots; // 메모리: [R0][R1][R2][R3]... // Position만 업데이트할 때도 전체 구조체가 캐시에 로드됨 for (Robot& R : Robots) { R.Position += R.Velocity; // Name 등 불필요한 데이터도 캐시 점유 }

Structure of Arrays (SoA) - 캐시 효율적

⭐ SoA - 캐시 효율적
코드 숨기기
// 데이터 지향 방식 struct RobotData { TArray<FVector> Positions; // [P0, P1, P2, P3, ...] TArray<FVector> Velocities; // [V0, V1, V2, V3, ...] TArray<float> Healths; // [H0, H1, H2, H3, ...] TArray<float> Shields; // [S0, S1, S2, S3, ...] TArray<FString> Names; // [N0, N1, N2, N3, ...] }; RobotData Data; // Position 업데이트: 연속된 메모리 접근 for (int32 i = 0; i < Data.Positions.Num(); ++i) { Data.Positions[i] += Data.Velocities[i]; // 캐시 히트 극대화! }

성능 비교 (Unreal Insights 측정)

📊 10,000개 Robot 업데이트 성능
AoS 방식:
7.38ms
SoA 방식:
1.18ms ⚡

SoA는 AoS보다 6배 빠름!

03

실전 적용

언제, 어떻게 SoA를 적용할까

SoA 적용이 효과적인 경우

대량의 동일 오브젝트

수백~수천 개의 파티클, 총알, NPC 등

부분 속성만 업데이트

Position만, 또는 Health만 자주 업데이트

단순 반복 연산

SIMD 벡터화가 가능한 수학 연산

실전 예시: 파티클 시스템 (Hot/Cold 분리)
코드 보기
struct ParticleSystemData { // Hot data - 매 프레임 접근 TArray<FVector> Positions; TArray<FVector> Velocities; TArray<float> LifeTimes; // Cold data - 가끔 접근 TArray<FLinearColor> Colors; TArray<float> Sizes; void UpdatePositions(float DeltaTime) { // 연속 메모리 접근으로 캐시 효율 극대화 const int32 Count = Positions.Num(); for (int32 i = 0; i < Count; ++i) { Positions[i] += Velocities[i] * DeltaTime; LifeTimes[i] -= DeltaTime; } } };
04

Mass Entity (ECS)

UE5의 데이터 지향 프레임워크

Mass는 UE5의 Entity Component System(ECS) 모듈로, CPU 메모리 캐싱 아키텍처에 최적화된 데이터 저장 방식을 사용합니다.

🏗️ Mass ECS 구성 요소

📦 Fragment

데이터 컴포넌트
(Position, Velocity 등)

🔖 Entity

Fragment들의 조합 ID
(Actor 대체)

⚙️ Processor

Fragment를 처리하는 시스템
(로직 담당)

📁 Archetype

동일 Fragment 조합의
엔티티 그룹

현재 상태

Mass는 Experimental 상태이지만, 대량의 동일 오브젝트(군중, 파티클 등)를 처리할 때 기존 Actor 방식보다 훨씬 효율적입니다. Fortnite의 군중 시스템에 사용됩니다.

SUMMARY

핵심 요약

  • Data Oriented Design: 데이터 배치를 CPU 캐시에 최적화
  • AoS (Array of Structures): 전통적 방식, 캐시 비효율적
  • SoA (Structure of Arrays): 캐시 효율적, 최대 6배 성능 향상
  • 적용 대상: 대량 동일 오브젝트, 부분 속성 업데이트, 단순 반복 연산
  • Mass Entity: UE5의 ECS 프레임워크 (Experimental)
Part 2 완료!

CPU 최적화를 마스터했습니다! 다음 Part에서는 GPU 최적화의 핵심인 Draw Call 최적화, Shader Complexity, Nanite/Lumen/VSM 최적화를 다룹니다.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 배열 순회 성능 비교

TArray와 TArray 두 가지로 10만 개 요소를 순회하는 벤치마크를 작성하세요. 연속 메모리(AoS) vs 포인터 간접 참조의 성능 차이를 SCOPE_CYCLE_COUNTER로 측정합니다.

실습 2: SoA 변환 실습

기존 AoS(Array of Structures) 패턴의 파티클 시스템을 SoA(Structure of Arrays) 패턴으로 변환하세요. 위치, 속도, 수명을 별도 배열로 분리하여 캐시 히트율 향상을 확인합니다.

심화 과제

1000개 이상의 프로젝타일 업데이트 시스템을 Data-Oriented 방식으로 재설계하세요. ISPC 또는 수동 SIMD를 활용하여 위치 업데이트를 벡터화하고, 기존 OOP 방식 대비 성능 향상을 측정합니다.