Unity DOTS, Bevy ECS, Flecs 메모리 친화적 구조 설계

메모리 친화적 설계가 중요한 이유

CPU 속도는 나날이 빨라지지만, 메모리 접근 속도는 여전히 병목의 중심이다. 이걸 가장 뼈저리게 체감하는 순간은, 매 프레임 수천 개의 엔티티를 순회할 때다. 그때 캐시 미스가 하나라도 터지는 순간, 프레임 드랍은 눈앞이다. Unity DOTS, Bevy ECS, Flecs 같은 데이터 지향 엔진들은 바로 이 문제에 정면 돌파해왔다. 그들의 출발점은 단순하다. “클래식 객체 지향 방식은 캐시를 버린 구조다.” 이걸 인식한 사람들과 아닌 사람들의 결과물은 완전히 다르다.

Entity-Component-System (ECS) 아키텍처 설계 및 최적화 👆

Unity DOTS의 메모리 접근 최적화 방식

Archetype 기반 Chunk 구조

Unity DOTS는 엔티티의 컴포넌트 조합을 기준으로 Archetype이라는 구조를 정의하고, 동일 Archetype을 공유하는 엔티티들을 Chunk라는 메모리 블럭에 연속적으로 저장한다. 이 Chunk는 캐시 라인 단위로 설계되어 있으며, 하나의 Chunk 안에는 최대 16KB의 컴포넌트 데이터가 밀집 저장된다.

이게 왜 중요하냐고? 연속된 데이터는 CPU 캐시 프리패칭(prefetching)의 핵심 요건이다. 즉, 다음에 사용할 데이터가 메모리 상으로 근처에 있으면, CPU가 미리 읽어들일 수 있어 캐시 미스를 줄일 수 있다. 실제로 Unity DOTS의 ECS 백서에서는 “Sequential layout of homogeneous data increases L1 cache utilization by up to 80%”(Unity DOTS Whitepaper, 2020)라고 밝히고 있다.

ComponentData와 IComponentData의 구분

Unity DOTS에서 IComponentData는 구조체(struct)로 설계되어야 한다. 이는 힙 할당이 아닌 스택 또는 연속 메모리 블록에 저장되도록 하기 위함이다. 클래스 형태로 선언되면, 가비지 컬렉터(GC)의 영향을 받고, 주소도 산발적으로 퍼진다. GC Heap은 캐시의 적이다. 반면 struct는 Stack Allocation 또는 NativeArray를 통해 연속적으로 배치할 수 있다.

SystemBase의 Burst Compiler 최적화

DOTS에서 SystemBaseIJobChunk를 사용해 로직을 분리하는 방식은 CPU에서 SIMD 명령어를 적극적으로 활용할 수 있게 한다. Unity의 Burst Compiler는 LLVM 기반으로, 메모리 접근 패턴이 최적화된 경우 AVX2까지 활용 가능하다. 다시 말해, 메모리 구조가 친화적이지 않으면 Burst의 성능도 뚝 떨어진다. 메모리 구조와 연산 로직은 함께 설계해야 하는 이유가 바로 여기에 있다.

LiveOps 시스템 구축 (A/B 테스트, 원격 설정, 클라우드 통계 기반 핫패치) 👆

Bevy ECS의 Sparse Set 기반 설계

SparseSet의 캐시 친화성

Bevy의 ECS는 Rust 언어 기반으로, 내부적으로는 SparseSet 구조를 통해 컴포넌트를 관리한다. 이는 Entity ID를 인덱스로 사용하는 Dense VecSparse Index의 조합으로 구성되며, 대부분의 iteration은 Dense 부분에서 일어난다. 이게 무슨 의미냐면, “Entity들이 메모리 상으로 연속되지 않아도, 컴포넌트 단위로는 연속될 수 있다”는 말이다. 이 구조는 SoA(Structure of Arrays) 방식과 매우 유사하다.

이 방식이 캐시에 얼마나 유리하냐면, Bevy 공식 문서에서도 “SparseSet iteration enables cache-local traversal of components even for unrelated entities”라고 설명하고 있다 (Bevy ECS Book, 2022). 그리고 실제로 Bevy 엔진은 매 프레임 수십만 개의 시스템 호출을 처리하면서도 60FPS를 유지하는 데 이 구조가 중요한 역할을 한다.

Query 중심의 데이터 접근

Bevy는 Query API를 통해 컴포넌트를 필터링하고 접근한다. 이 때 중요한 포인트는, Query 결과는 내부적으로 정렬된 iterator로 구성된다는 것이다. 즉, Query를 통해 접근하는 컴포넌트들은 이미 캐시 적중률을 최대화하기 위한 순서로 정렬된다는 말이다. 이것은 메모리 접근을 단순히 필터링이 아닌 “순서 최적화”의 관점으로 본 것이다.

EntityCommandBuffer의 Deferred Write 전략

Bevy는 ECS의 변화를 즉시 메모리에 반영하지 않고, EntityCommandBuffer에 저장한 뒤, Frame 끝에서 일괄 반영한다. 이 덕분에 불필요한 캐시 인밸리데이션(invalidation)이 줄어든다. 특히 다중 쓰레드 환경에서 이 구조는 치명적인 성능 차이를 만든다. 메모리 친화적이라기보다는, 쓰기 연산 자체를 회피하는 전략이지만, 전체 시스템 메모리 활용도에는 긍정적이다.

모바일 게임 성능 최적화 (Battery Drain, GC, Frame Drop 분석) 👆

Flecs의 SoA 기반 데이터 배치 전략

Structure of Arrays와 Cache Line 활용

Flecs는 C 언어 기반으로 만들어졌지만, 내부 구조는 상당히 현대적이다. 특히 메모리 구조는 SoA(Structure of Arrays) 방식을 극단적으로 밀어붙이고 있다. 각 컴포넌트 타입별로 연속된 메모리 공간을 유지하며, 하나의 엔티티에 속한 각 컴포넌트들은 서로 다른 배열에 저장된다.

이게 왜 유리할까? 예를 들어 위치(Position)만 업데이트하는 물리 시스템이 있다고 치자. OOP 방식에서는 객체 전체를 순회해야 하고, 컴포넌트 접근 시마다 포인터를 따라가야 한다. 반면 SoA에서는 Position 배열만 순회하면 된다. CPU는 순차 메모리 접근에 특화되어 있기 때문에, 이 구조는 L1/L2 캐시 효율을 극대화한다. 실제로 ECS 성능 비교 벤치마크(ECS Performance Report, Embark Studios, 2021)에 따르면, SoA 기반 엔진은 AoS(Object of Structs)보다 최대 4.2배 빠른 처리를 보여줬다.

Table-based Storage와 Signature Matching

Flecs는 엔티티의 컴포넌트 구성을 Signature라는 형태로 기록하고, 동일 Signature를 가진 엔티티들을 같은 테이블에 저장한다. 이 테이블은 내부적으로 Struct of Arrays 구조를 갖는다. Signature를 기준으로 필터링하면, 이미 메모리 상에 정렬된 테이블만 조회하게 된다. 즉, 쿼리 자체가 곧 “정렬된 연속된 메모리 접근”이라는 뜻이다.

게다가 이 테이블은 Compact 하게 설계되어 있어서, 컴포넌트 추가/삭제 시 메모리 재정렬이 거의 없다. 다시 말해, 메모리 파편화(Fragmentation)가 최소화된다. 이건 GC 없는 환경에서 특히 중요한 이점이다.

Pipeline과 Stage 기반 멀티스레딩 최적화

Flecs는 Pipeline 개념을 통해 System을 순차적 또는 병렬로 구성할 수 있고, 각 Stage는 독립적인 데이터 세트를 참조한다. 이 구조 덕분에 메모리 공유 충돌이 거의 없고, 쓰레드별 캐시 효율이 증가한다. 단순히 CPU 코어를 많이 사용하는 것이 아니라, 그 메모리가 “충돌 없이 잘 분리되어야” 진짜 성능이 나온다. Flecs는 이 점을 굉장히 잘 설계한 대표적인 예다.

세이브/로드 시스템의 직렬화 최적화 (Binary Serialization, JSON/Protobuf 기반 비교) 👆

결론

Unity DOTS, Bevy ECS, Flecs가 추구하는 방향은 단순한 성능 향상이 아니라, 메모리를 바라보는 사고방식의 전환에 가깝다. 객체 지향 방식의 느슨한 구조에서 벗어나 데이터를 연속적으로 배치하고, CPU 캐시 효율을 극대화하는 관점은 이제 선택이 아니라 필수다. 매 프레임 수천, 수만 개의 엔티티를 다루는 환경에서 한 번의 캐시 미스가 어떤 결과를 만드는지 직접 경험해본 사람이라면, 왜 이들이 이토록 메모리 구조를 집요하게 다듬는지 충분히 공감할 수 있을 것이다. 실제로 Unity DOTS의 공식 백서(Unity DOTS Whitepaper, 2020), Bevy ECS 문서(Bevy ECS Book, 2022), Embark Studios ECS 성능 비교 리포트(Embark Studios, 2021)는 모두 동일한 결론에 도달한다. 성능은 알고리즘만이 아니라 메모리 구조에서 시작된다. 그리고 결국 좋은 설계는 기능을 넘어 경험을 바꾼다. 프레임 드랍이 사라지고, 시스템이 가볍게 호흡하듯 움직일 때, 개발자는 그제야 데이터 지향적 설계가 가진 진짜 힘을 체감한다. 이 여정은 멀고도 깊지만, 한번 이해하고 나면 절대 되돌아갈 수 없다.

Vulkan/DirectX 12 기반의 저수준 그래픽스 렌더링 파이프라인 👆

FAQ

ECS가 객체 지향 방식보다 무조건 빠른가?

항상 그렇지는 않다. 엔티티 수가 적거나, 매 프레임 반복 연산이 많지 않은 게임이라면 OOP 방식도 충분히 효율적이다. 다만 엔티티 수가 급격히 증가하는 순간, 캐시 미스로 인한 지연이 드러나면서 ECS의 이점이 극적으로 나타난다. 결국 선택 기준은 규모와 목적이다.

Unity DOTS의 Chunk 구조가 실제로 얼마나 효과적인가?

Unity DOTS 백서(Unity DOTS Whitepaper, 2020)는 Chunk 기반 구조가 L1 캐시 활용도를 최대 80%까지 향상시킬 수 있다고 설명한다. 이는 과장된 수치가 아니라, 실제 프로젝트에서 수천 개의 엔티티가 매 프레임 처리될 때 체감할 수 있는 수준의 차이다.

Bevy ECS의 SparseSet 방식은 어떤 상황에서 가장 유리한가?

특정 시스템이 일부 컴포넌트만 집중적으로 다루는 경우 특히 강력하다. 예를 들어 Transform만 갱신하는 시스템이 있을 때, SparseSet은 Position 데이터만 밀집 저장하므로 빠르게 순회할 수 있다. FOV나 AI처럼 필터링된 접근이 많은 게임에서도 효과적이다.

Flecs의 SoA 방식은 왜 중요한가?

SoA는 캐시 프리패칭과 순차적 접근을 극대화한다. Embark Studios의 ECS 벤치마크 결과(2021)에 따르면 SoA 구조는 AoS 구조 대비 최대 4.2배의 성능 향상을 보였다. 특히 CPU가 다음 데이터를 예측할 수 있는 상황에서 이 차이가 커진다.

ECS 구조는 멀티스레딩을 자동으로 해결해주는가?

아니다. 멀티스레딩은 설계와 동기화 철학에 따라 완전히 다른 결과를 만든다. Unity DOTS의 Job System, Bevy의 Command Buffer, Flecs의 Pipeline처럼 각 엔진이 다른 전략을 갖고 있지만, 결국 병렬 접근을 안전하게 보장하는 구조가 필요하다.

ECS 구조를 적용할 때 가장 어려운 점은 무엇인가?

초기 설계의 관점 변화다. OOP에서 컴포넌트를 붙이고 메서드를 호출하는 방식에 익숙하면 ECS의 데이터 우선 사고방식이 굉장히 낯설다. 하지만 일단 전환하면 시스템 전체의 단순화와 확장성에서 큰 자유를 얻는다.

ECS가 게임 개발에서만 유용한가?

절대 아니다. 대규모 시뮬레이션, 로보틱스 제어, 금융 데이터 처리 등에서도 ECS는 강력한 선택지가 된다. 대량의 데이터와 반복 연산이 있는 곳이라면 어디든 효과가 있다.

ECS 구조는 초보자에게 적합한가?

처음엔 진입 장벽이 높다. 그러나 명확한 개념과 작은 실험부터 시작하면 충분히 익숙해질 수 있다. 오히려 한 번 익숙해지고 나면 OOP 방식으로 돌아가는 것이 답답하게 느껴질 수 있다.

ECS 시스템 간 데이터 공유는 어떻게 신중하게 처리해야 하는가?

데이터 공유는 캐시 인밸리데이션을 유발할 수 있으므로 최소화해야 한다. Bevy의 Command Buffer나 Unity DOTS의 EntityCommandBuffer처럼 Deferred Write 전략을 사용하면 성능 저하를 줄일 수 있다.

ECS 선택 기준은 무엇인가?

팀 규모, 엔티티 수, 운영 환경, 멀티스레딩 여부, 개발 속도 등을 종합적으로 고려해야 한다. 작은 팀이 Bevy를 선택해 빠르게 프로토타이핑할 수도 있고, 대규모 프로젝트가 Unity DOTS를 선택해 성능을 극한까지 끌어올릴 수도 있다. 정답은 없다. 목적이 결정한다.

애니메이션 상태기반 블렌딩 (State Machine Behaviours, Motion Matching) 👆
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments