ݺߣ

ݺߣShare a Scribd company logo
2020-05-09 제 540회 Dev-Rookie 발표자 이석우
Data-Oriented Design and Unity DOTS
DOD의 기원을 설명하기 위해선 먼저 컴퓨터 하드웨어에 대한 이해가 필요하다.
고질적인 메모리 병목 현상
• CPU 속도는 계속해서 빨라졌지만 데이터를 가져오는 건 그다지 빨라지지 않았다.
너와 나의 속도 차이~
지역성의 원리(Principle of Locality)
• 시간적 지역성 : 최근 참조된 메모리 주소는 가까운 미래에 다시 한번 참조되는 경향
• ex) 반복문, 방금 사용한 명령어와 데이터에 반복해서 접근
• 공간적 지역성 : 최근 참조된 메모리 주소의 이웃이 가까운 미래에 참조되는 경향
• ex) 배열 순회
• 오늘날의 컴퓨터는 이 지역성을 고려하여 메모리 병목 현상을 최소화 한다.
메모리 구조
• 대부분의 데이터는 메모리에 적재되고, 그 중 많이 참조되는 데이터는 캐시로 가져온다.
레지스터
캐시
메인 메모리
디스크
속도 및 가격 용량
CPU Core
L2 Cache
Main Memory
10 cycles 250 cycles
메인 메모리가 L2 Cache에 약 25배 정도 느리다.
– Intel Xeon 5500 -
캐시 미스와 프로그램 성능
• 캐시 미스가 발생할수록 프로그램은 느려진다.
CPU Core
Cache
Memory
(RAM)
데이터 줘
여기 있다
응 없어(Cache miss)
느리지만 줄게
캐시 미스를 최소화하기 위해선
어떻게 코드를 짜야 할까?
과거의 CPU 개발
• 싱글 코어만 사용하여 성능을 높이기 위해서 클럭 속도를 높이는 방법을 주로 사용
했다.
• 그러나 클럭을 높이면 전력 소모량이 많아지는 것은 물론 발열까지 심해진다.
멀티코어 프로세서
• 따라서 오늘날의 클럭 속도를 높이는 대신 코어 수를 늘리는 방식으로 CPU를 개발한다.
Program
Multi-core
Core 1
Core 2
Core 3
Core 4
threads
즉, 프로그램의 성능을 최대한 끌어내기 위해선 모든 코어를 사용해야 한다.
Data-Oriented Design과 유니티 DOTS
멀티쓰레딩은 어렵다…
이것을 쉽고 효율적으로 패턴화시킬
방법은 없을까?
Data-Oriented Design
• 오늘날 컴퓨터 하드웨어에 최적화된 디자인 패턴
• 캐시 미스를 최소화하고 멀티쓰레딩을 하기 쉽게 코드를 짤 수 있다.
고려 사항
성능이 문제가
있는가?
프로그램 실행
자주 실행되는
코드인가?
DOD 적용
성능 문제가
캐시 때문인가?
Yes
Yes
Yes
기존 OOP
No
No
No
OOP를 이용한 게임 로직 구현
• 게임 엔티티는 AI, 물리, 렌더 컴포넌트를 가진다.
각 컴포넌트에는 상태를 업데이트하기 위한 메서드가 들어있다.
매번 게임 루프를 돌 때마다 각 컴포넌트를 업데이트 한다.
Entity array Entity
AI
물리
렌더링
Entity
Entity
Entity
물리
렌더링
AI
물리
렌더링
물리
AI
렌더링
AI
OOP의 문제점
① 게임 엔티티를 포인터로 접근
캐시 미스 발생
② 게임 엔티티의 컴포넌트를 포인터로 접근
캐시 미스 발생
③ 컴포넌트를 업데이트한다.
④ 모든 엔티티에 대해 같은 작업을 반복한다.
따라서 사실상 위와 같은 의미를 지닌다.
DOD 적용
• 각 컴포넌트 별로 배열을 생성하여 객체를 담는다.
AI
렌더링
물리
AI AI AI
물리 물리 물리
렌더링 렌더링 렌더링
각 컴포넌트 객체를 배열로 생성하면 메모리 상에서 일렬로 생성된다.
데이터를 캐시로 옮길 때 주변 데이터도 블록 단위로 함께 옮겨지므로 캐시 미스가 줄어든다.
DOD 적용
OOP VS DOD TEST
• DOD 방식 : 9.4320ms
• Entity에 컴포넌트 배열을 데이터로 가지고 있음 :
• Entity가 컴포넌트 배열을 포인터로 접근 :
TEST CODE : https://github.com/munificent/game-programming-patterns/blob/master/code/structure-of-arrays/component_update/main.cpp
속도 배
컴토넌트 배열 순회 9.4320ms
엔티티 배열 순회 ( 엔티티에 컴포넌트 배열을 포함) 15.5460ms 1.65x
엔티티 배열 순회 ( 엔티티에 컴포넌트 배열을 포인터로 접근) 15.7990ms 1.68x
Order by entity 22.4630ms 2.38x
Order by components 13.0540ms 1.38x
랜덤 컴포넌트 배열 순회 89.5480ms 9.49x
랜덤 엔티티 배열 순회 149.9980ms 15.90x
처음 방법이 DOD 방식이고 맨 아래가 일부러 캐시 미스를 발생시킬 때 속도 차이이다.
위의 표는 한 가지 타입의 컴포넌트로 테스트 했고 네 가지 타입으로 테스트하면 최대 50
배 정도의 성능 차이가 발생했다.
DOD 적용 사례
Parallelizing Conqueror's Blade
Culling the Battlefield
: Data Oriented Design in Practice
Data-Oriented Design and C++
저거 다 설명하려면 일주일 내내 발표해야 함.
대충 유니티만 알아봅시다.
얘들아 나 유니티 코펜하겐
갔다왔어. 거기서 DOTS
발표하더라올ㅋ 그래봤자 유니티…
니들은 언리얼 성님 못따라
간다
- 평화로운 스터디 중 어느 날 -
- 코로나 사태로 쉬던 중 -
아 심심해 뭐할꺼 없나…
그때 알콜코더님이 말씀하신
발표나 볼까?
이건 발표 해야만 해
헐?!
이럴수가
Unity DOTS
• Data-Oriented Tech Stack
그냥 DOD라 하면 없어 보이잖아?
• 핵심 기능
ECS Model
Job System
Burst Compiler
ECS Model
• Entity Component System
Entity = ID
Component = Data
Component System = Behavior
Entity A Entity B Entity C
System
Translation
Rotation
LocalToWorld
Renderer
Translation
Rotation
LocalToWorld
Renderer
Translation
Rotation
LocalToWorld
L2W = T * R
ECS Model
AI Data
Rendering Data
Physics Data
Rendering Data Rendering Data Rendering Data
AI Data
Physics Data
AI Data
Physics Data
AI Data
Physics Data
Entity는 개념적으로만 컴포넌트를 소유하고 실제로는 ID만 가지고 있다.
Entity
0
Entity
1
Entity
2
Entity
3
AI Component Array
Physics Component Array
Render Component Array
Update AI Data
AI Component System
Update Physics Data
Physics Component System
Update Rendering Data
Rendering Component System
Entity
Entity 실제 구현부를 보면 Index(ID)와 Version 관련 변수를 소유하고 있을 뿐,
Component를 직접적으로 소유하고 있지 않다.
Archetype N
Archetype
• 컴포넌트의 조합에 따라 Archetype으로 분류된다.
Entity A, B는 Archetype M인 컴포넌트 조합을 소유한 반면,
Entity C는 Archetype N인 컴포넌트 조합을 소유한다.
Entity A Entity B Entity C
Translation
Rotation
LocalToWorld
Renderer
Translation
Rotation
LocalToWorld
Renderer
Translation
Rotation
LocalToWorld
Archetype M
Memory Chunks
• 컴포넌트 배열은 Archetype 별로 생성된다.
• 하나의 배열에 모든 컴포넌트를 담지 않고 16K 바이트로 쪼개어 담는다.
ArcheType
16K Chunk
Position Array
Rotation Array
Renderer Array
16K Chunk
Position Array
Rotation Array
Renderer Array
16K Chunk
Position Array
Rotation Array
Renderer Array
ArcheType
16K Chunk
Position Array
Renderer Array
16K Chunk
Position Array
Renderer Array
16K Chunk
Position Array
Renderer Array
World
• World는 하나의 EntityManager 객체와 System Group을 포함하는 개념이다.
World
Entity Manager
System Group
World
Entity Manager
System Group
World
Entity Manager
System Group
Job System
• 사용자가 쓰기 쉽도록 구현된 멀티쓰레딩 라이브러리
Parallelizing the Naughty Dog Engine Using Fibers(2015) - GDC Vault
Job System : Terms
Job : 실행할 작업
Worker Thread
Fiber : Job을 실행하기 위한 Context ( User provided stack, register)
Worker Thread
Worker Thread : Job을 실행할 유닛
Worker Thread
Job System : Execute Job
CPU
core
core
core
core
Worker Thread
Worker Thread
Worker Thread
Fiber Pool
Job Queue
※Worker Thread는 코어에 고정됨 ( 쓰레드 간의 컨텍스트 스위칭이 발생하지 않는다.)
Job 실행하기 위해 먼저 Fiber Pool에서
Fiber 하나를 Worker Thread에 가져온다.
CPU
core
core
core
core
Worker Thread
Worker Thread
Worker Thread
Job Queue
아직 Job이 없는 Worker Thread의 Fiber에 Job을 삽입한다.
(Job은 Fiber 안에서만 실행한다.)
CPU
core
core
core
core
Worker Thread
Worker Thread
Worker Thread
Job Queue
Job은 실행 중 새로운 잡을 생성할 수도 있다.
Job System : Synchronization
CPU
core
core
core
core
Worker Thread
Worker Thread
Worker Thread
Wait List
1
실행 중 대기 상태가 된 Job은 Wait List에 옮겨져
Wait List의 카운터가 갱신될 때까지 대기한다.
(Fiber와 함께 옮겨져 실행에 필요한 Context은 보존된다.)
CPU
core
core
core
core
Worker Thread
Wait List
1
Worker Thread에는 새로운 Fiber가 들어가 다른 Job을 실행한다.
Fiber Pool
Job Queue
Worker Thread
Wait List
0
대기 상태의 잡과 동기화에 관련된 잡이 완료되면,
대기 리스트의 카운터를 0으로 만들고 파이버는 풀로 반환된다.
Fiber Pool
CPU
core
core
core
core
Worker Thread
Wait List
0
대기 상태였던 Job은 다시 옮겨져 Worker Thread에서 실행된다.
Job System에서 유일한 동기화 방법이다.
Job System
CPU
core
core
core
core
Worker Thread
Worker Thread
Worker Thread
Job Queue
Race Condition
0Thread A + 1
0 + 1Thread B
0Shared variable 1 1
read read write write
메인 쓰레드의 데이터에 대한 레퍼런스를 Thread A, B가 동시에 접근하는 경우 경쟁 상태가 발생한다.
non-mutually
exclusive
operations
cause error
interrupted read/write
Job Safety
Unity DOTS : Job System
0 + 1Thread A
1 + 1Thread B
Data (In) 1
read read writewrite
operations
mutually
exclusive,
non-interrupted
Shared Data (Out) 1
0
2
Schedule
dependency
Blittable
NativeContainers
data copy
data copy
Job Safety
• 잡 시스템은 메인 스레드의 데이터에 대한 레퍼런스를 잡에 보내지 않고, 데이터의 복
사본을 보낸다.
• 이 복사본은 해당 잡 안에서만 사용하므로 경쟁 상태가 발생하지 않는다.
• 잡은 NativeContainer에 결과를 저장하여 데이터를 공유할 수 있다.
• 잡 간의 실행 순서는 실행 예약(schedule)을 통해 정한다. (Schedule dependency)
Burst Compiler
• 최적화된 Native code를 생성해주는 컴파일러
• 모노 컴파일러로 컴파일한 코드는 C++에 비해 최대 10~30배 정도 느리다.
• 버스트 컴파일러는 IL 코드를 C++로 만들고 이를 Native code로 변환한다.
Behind the Burst Compiler
Burst Compiler의 제약
• Job System 내에서만 사용할 수 있다.
• Managed 객체(C# 클래스)를 사용할 수 없다.
기존 유니티 방식
• MonoBehavior 기반 클래스에 데이터와 행동을 정의
MonoBehaviour
• Data
• Behavior
MonoBehaviour
• Data
• Behavior
MonoBehaviour
• Data
• Behavior
MonoBehaviour
• Data
• Behavior
Pure ECS
• 순수 ECS는 ComponentData 구조체에 데이터를 정의하고 시스템에서 행동을 정의
IComponentData
• Data
IComponentData
• Data
IComponentData
• Data
IComponentData
• Data
ComponentSytem
• Behaviour
Hybrid ECS
• Hybrid ECS는 MonoBehavior 기반 클래스에 데이터만 정의하고 시스템에서 행동을 정의
MonoBehaviour
• Data
MonoBehaviour
• Data
MonoBehaviour
• Data
MonoBehaviour
• Data
ComponentSytem
• Behaviour
Component System 실행 순서
• Component System은 Initialization, Simulation, Presentation 단계 중 어디에 위치할
지 선택할 수 있다. (Default : Simulation)
각 단계 별로 System Group을 소유하고 있으며,
개발자가 정의한 System의 실행 순서는 원하는 대로 지정할 수 있다.
그러면 그에 맞게 Component System의 OnUpdate 함수가 호출 된다.
Initialization Simulation Presentation
Component System 실행 순서
참고로 Monobehavior 객체의 Update 함수 호출 시기는 위와 같다.
Initialization Simulation Presentation
FixedUpdate()
Update() LateUpdate()
정리
• Data Oriented-Design은 현대 하드웨어에 친화적인 디자인 패턴
• 유니티는 DOD를 적용하기 위해 Dots 도입
ECS
Jobsystem
Burst Compiler
• 아직은 과도기이고 적용 사례가 빈약하지만 향후 기대
ECS Example
큐브를 만들어 Rotator라는 스크립트를 추가하고 코드를 연다.
열어보면 다음과 같은 코드가 보인다.
시간에 따라 회전하도록 만들려면 기존에는 위와 같이 코드를 작성한다.
이제 ECS 방식으로 만들기 위해 Data와 Behaviour를 분리한다.
Rotator와 Transform 컴포넌트를 가진 엔티티를 불러와 회전시킨다.
Data-Oriented Design과 유니티 DOTS
그러나 실행해보면 큐브는 회전하지 않는다. 그 이유는 큐브가 엔티티가 아닌 게임 오브젝트이기 때문이다.
따라서 큐브에 Convert To Entity 컴포넌트를 추가해 주면 이 문제가 쉽게 해결된다.
Job System : Define Job
Job은 구조체로 IJob을 상속 받아 정의한다.
Execute 함수의 for문은 쓰레드 하나에서 모두 처리된다.
IJobParallelFor를 이용하면 For문이 병렬화 되어 모든
Worker 쓰레드로 분할되어 처리된다.
게임 오브젝트의 Transform을 변환하고 싶다면 IJobParallelForTransform을 이용한다.
Job System : Create Job
Start Job
Job의 실행을 예약하려면 위와 같이 선언하면 된다.
스케줄 함수의 매개변수로 잡핸들을 넣으면 그 핸들의 잡이 처리된 후 시작하도록 예약한다.
- 스케줄 종속성(Schedule dependency) -
JobParallelFor의 핸들의 스케줄 함수는 조금 더 복잡하다.
첫번째 매개변수는 For의 반복 횟수를 의미하고,
두번째는 각 Worker Thread당 몇 개의 잡을 처리할지를 의미한다.
Transform 객체의 managed array를 생성하고 TransformAccessArray에 랩핑한다.
IJobParallelForTransform 기반 객체는 이 랩핑한 배열을 schedule의 매개변수로 받아와 처리한다.
모든 잡에 대한 스케줄을 짰으면 이를 잡시스템에 알린다.
그러면 잡시스템은 잡을 처리한다.
Schedule과 ScheduleBatchedJobs를 이용하지 않고 잡 객체의 Run 함수를 이용하여 잡을
처리할 수도 있다.
JobSystem : Finish Job
Job System : Example
Data-Oriented Design과 유니티 DOTS
Data-Oriented Design과 유니티 DOTS
Data-Oriented Design과 유니티 DOTS
Data-Oriented Design과 유니티 DOTS

More Related Content

What's hot (20)

PPTX
언리얼을 활용한 오브젝트 풀링
TonyCms
PDF
테라로 살펴본 MMORPG의 논타겟팅 시스템
QooJuice
PPTX
NDC 11 자이언트 서버의 비밀
승명 양
PPTX
서비스중인 게임 DB 설계 (쿠키런 편)
_ce
PDF
ٰ12峢dz게임서버설계왶구현
noerror
PDF
임태현, 게임 서버 디자인 가이드, NDC2013
devCAT Studio, NEXON
PPTX
리플렉션과 가비지 컬렉션
QooJuice
PPT
Multithread & shared_ptr
내훈 정
PPTX
게임프로젝트에 적용하는 GPGPU
YEONG-CHEON YOU
PDF
조정훈, 게임 프로그래머를 위한 클래스 설계, NDC2012
devCAT Studio, NEXON
PDF
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
devCAT Studio, NEXON
PDF
멀티스레드 렌더링 (Multithreaded rendering)
Bongseok Cho
PPTX
Ndc14 분산 서버 구축의 ABC
Ho Gyu Lee
PPTX
GameInstance에 대해서 알아보자
TonyCms
PDF
전형규, M2 클라이언트 스레딩 아키텍쳐, NDC2013
devCAT Studio, NEXON
PDF
게임서버프로그래밍 #0 - TCP 및 이벤트 통지모델
Seungmo Koo
PDF
중앙 서버 없는 게임 로직
Hoyoung Choi
PDF
행동 트리
Sukwoo Lee
PDF
Visual Studio를 이용한 어셈블리어 학습 part 1
YEONG-CHEON YOU
PDF
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
내훈 정
언리얼을 활용한 오브젝트 풀링
TonyCms
테라로 살펴본 MMORPG의 논타겟팅 시스템
QooJuice
NDC 11 자이언트 서버의 비밀
승명 양
서비스중인 게임 DB 설계 (쿠키런 편)
_ce
ٰ12峢dz게임서버설계왶구현
noerror
임태현, 게임 서버 디자인 가이드, NDC2013
devCAT Studio, NEXON
리플렉션과 가비지 컬렉션
QooJuice
Multithread & shared_ptr
내훈 정
게임프로젝트에 적용하는 GPGPU
YEONG-CHEON YOU
조정훈, 게임 프로그래머를 위한 클래스 설계, NDC2012
devCAT Studio, NEXON
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
devCAT Studio, NEXON
멀티스레드 렌더링 (Multithreaded rendering)
Bongseok Cho
Ndc14 분산 서버 구축의 ABC
Ho Gyu Lee
GameInstance에 대해서 알아보자
TonyCms
전형규, M2 클라이언트 스레딩 아키텍쳐, NDC2013
devCAT Studio, NEXON
게임서버프로그래밍 #0 - TCP 및 이벤트 통지모델
Seungmo Koo
중앙 서버 없는 게임 로직
Hoyoung Choi
행동 트리
Sukwoo Lee
Visual Studio를 이용한 어셈블리어 학습 part 1
YEONG-CHEON YOU
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
내훈 정

Similar to Data-Oriented Design과 유니티 DOTS (20)

PPTX
Visual C++10을 활용한 병렬 프로그래밍
흥배 최
PPT
Data oriented design
Sangwook Kwon
PDF
Project anarchy로 3d 게임 만들기 part_3_움직여라 움직여
Dong Chan Shin
PPTX
Deview2013 - 게임기술과 수퍼컴퓨팅의 공생관계
Tae Yong Kim
PDF
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
Kiheon Park
PDF
Direct x 12 초기화
QooJuice
PDF
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
funmeate
PPTX
Game Play System
changehee lee
PDF
병렬프로그래밍과 Cuda
Seok-joon Yun
PDF
Ndc2010 김주복, v3. 마비노기2아키텍처리뷰
Jubok Kim
PDF
06게임엔진구성
noerror
PPTX
Cuda intro
주영 송
PPTX
면접 대비 벡터, CS 개념과 사례
Soochan Park
PPTX
CUDA 프로그래밍 기초 MODUCON2018
Shengzhe Li
PDF
Project anarchy로 3d 게임 만들기 part_4_network_memory management
Dong Chan Shin
PPTX
Component-Based Entity System과 Data-oriented Design
Sukwoo Lee
PPTX
[KGC2014] DX9에서DX11로의이행경험공유
Hwan Min
PPTX
Game AI 기초 [ 유니티 및 C# 스터디 / 2024-03-22 ]
leusin2
PPT
ٰ11깶성익슈퍼클래스
Sungik Kim
PDF
브릿지 Unity3D 기초 스터디 2회
BridgeGames
Visual C++10을 활용한 병렬 프로그래밍
흥배 최
Data oriented design
Sangwook Kwon
Project anarchy로 3d 게임 만들기 part_3_움직여라 움직여
Dong Chan Shin
Deview2013 - 게임기술과 수퍼컴퓨팅의 공생관계
Tae Yong Kim
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
Kiheon Park
Direct x 12 초기화
QooJuice
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
funmeate
Game Play System
changehee lee
병렬프로그래밍과 Cuda
Seok-joon Yun
Ndc2010 김주복, v3. 마비노기2아키텍처리뷰
Jubok Kim
06게임엔진구성
noerror
Cuda intro
주영 송
면접 대비 벡터, CS 개념과 사례
Soochan Park
CUDA 프로그래밍 기초 MODUCON2018
Shengzhe Li
Project anarchy로 3d 게임 만들기 part_4_network_memory management
Dong Chan Shin
Component-Based Entity System과 Data-oriented Design
Sukwoo Lee
[KGC2014] DX9에서DX11로의이행경험공유
Hwan Min
Game AI 기초 [ 유니티 및 C# 스터디 / 2024-03-22 ]
leusin2
ٰ11깶성익슈퍼클래스
Sungik Kim
브릿지 Unity3D 기초 스터디 2회
BridgeGames
Ad

More from Sukwoo Lee (8)

PDF
진화하는 컴퓨터 하드웨어와 게임 개발 기술의 발전
Sukwoo Lee
PDF
Cascade Shadow Mapping
Sukwoo Lee
PPTX
Bump Mapping
Sukwoo Lee
PPTX
포인트 셰도우
Sukwoo Lee
PDF
2018.12.22 깊이 버퍼 그림자 매핑
Sukwoo Lee
PPTX
2018.02.03 이미지 텍스처링
Sukwoo Lee
PPTX
리얼타임 렌더링 - 조명 입문편 -
Sukwoo Lee
PDF
2017 12 09_데브루키_리얼타임 렌더링_입문편(3차원 그래픽스[저자 : 한정현] 참조)
Sukwoo Lee
진화하는 컴퓨터 하드웨어와 게임 개발 기술의 발전
Sukwoo Lee
Cascade Shadow Mapping
Sukwoo Lee
Bump Mapping
Sukwoo Lee
포인트 셰도우
Sukwoo Lee
2018.12.22 깊이 버퍼 그림자 매핑
Sukwoo Lee
2018.02.03 이미지 텍스처링
Sukwoo Lee
리얼타임 렌더링 - 조명 입문편 -
Sukwoo Lee
2017 12 09_데브루키_리얼타임 렌더링_입문편(3차원 그래픽스[저자 : 한정현] 참조)
Sukwoo Lee
Ad

Data-Oriented Design과 유니티 DOTS

  • 1. 2020-05-09 제 540회 Dev-Rookie 발표자 이석우 Data-Oriented Design and Unity DOTS
  • 2. DOD의 기원을 설명하기 위해선 먼저 컴퓨터 하드웨어에 대한 이해가 필요하다.
  • 3. 고질적인 메모리 병목 현상 • CPU 속도는 계속해서 빨라졌지만 데이터를 가져오는 건 그다지 빨라지지 않았다. 너와 나의 속도 차이~
  • 4. 지역성의 원리(Principle of Locality) • 시간적 지역성 : 최근 참조된 메모리 주소는 가까운 미래에 다시 한번 참조되는 경향 • ex) 반복문, 방금 사용한 명령어와 데이터에 반복해서 접근 • 공간적 지역성 : 최근 참조된 메모리 주소의 이웃이 가까운 미래에 참조되는 경향 • ex) 배열 순회 • 오늘날의 컴퓨터는 이 지역성을 고려하여 메모리 병목 현상을 최소화 한다.
  • 5. 메모리 구조 • 대부분의 데이터는 메모리에 적재되고, 그 중 많이 참조되는 데이터는 캐시로 가져온다. 레지스터 캐시 메인 메모리 디스크 속도 및 가격 용량
  • 6. CPU Core L2 Cache Main Memory 10 cycles 250 cycles 메인 메모리가 L2 Cache에 약 25배 정도 느리다. – Intel Xeon 5500 -
  • 7. 캐시 미스와 프로그램 성능 • 캐시 미스가 발생할수록 프로그램은 느려진다. CPU Core Cache Memory (RAM) 데이터 줘 여기 있다 응 없어(Cache miss) 느리지만 줄게
  • 8. 캐시 미스를 최소화하기 위해선 어떻게 코드를 짜야 할까?
  • 9. 과거의 CPU 개발 • 싱글 코어만 사용하여 성능을 높이기 위해서 클럭 속도를 높이는 방법을 주로 사용 했다. • 그러나 클럭을 높이면 전력 소모량이 많아지는 것은 물론 발열까지 심해진다.
  • 10. 멀티코어 프로세서 • 따라서 오늘날의 클럭 속도를 높이는 대신 코어 수를 늘리는 방식으로 CPU를 개발한다.
  • 11. Program Multi-core Core 1 Core 2 Core 3 Core 4 threads 즉, 프로그램의 성능을 최대한 끌어내기 위해선 모든 코어를 사용해야 한다.
  • 13. 멀티쓰레딩은 어렵다… 이것을 쉽고 효율적으로 패턴화시킬 방법은 없을까?
  • 14. Data-Oriented Design • 오늘날 컴퓨터 하드웨어에 최적화된 디자인 패턴 • 캐시 미스를 최소화하고 멀티쓰레딩을 하기 쉽게 코드를 짤 수 있다.
  • 15. 고려 사항 성능이 문제가 있는가? 프로그램 실행 자주 실행되는 코드인가? DOD 적용 성능 문제가 캐시 때문인가? Yes Yes Yes 기존 OOP No No No
  • 16. OOP를 이용한 게임 로직 구현 • 게임 엔티티는 AI, 물리, 렌더 컴포넌트를 가진다.
  • 17. 각 컴포넌트에는 상태를 업데이트하기 위한 메서드가 들어있다.
  • 18. 매번 게임 루프를 돌 때마다 각 컴포넌트를 업데이트 한다.
  • 20. OOP의 문제점 ① 게임 엔티티를 포인터로 접근 캐시 미스 발생 ② 게임 엔티티의 컴포넌트를 포인터로 접근 캐시 미스 발생 ③ 컴포넌트를 업데이트한다. ④ 모든 엔티티에 대해 같은 작업을 반복한다. 따라서 사실상 위와 같은 의미를 지닌다.
  • 21. DOD 적용 • 각 컴포넌트 별로 배열을 생성하여 객체를 담는다. AI 렌더링 물리 AI AI AI 물리 물리 물리 렌더링 렌더링 렌더링 각 컴포넌트 객체를 배열로 생성하면 메모리 상에서 일렬로 생성된다. 데이터를 캐시로 옮길 때 주변 데이터도 블록 단위로 함께 옮겨지므로 캐시 미스가 줄어든다.
  • 23. OOP VS DOD TEST • DOD 방식 : 9.4320ms • Entity에 컴포넌트 배열을 데이터로 가지고 있음 : • Entity가 컴포넌트 배열을 포인터로 접근 : TEST CODE : https://github.com/munificent/game-programming-patterns/blob/master/code/structure-of-arrays/component_update/main.cpp 속도 배 컴토넌트 배열 순회 9.4320ms 엔티티 배열 순회 ( 엔티티에 컴포넌트 배열을 포함) 15.5460ms 1.65x 엔티티 배열 순회 ( 엔티티에 컴포넌트 배열을 포인터로 접근) 15.7990ms 1.68x Order by entity 22.4630ms 2.38x Order by components 13.0540ms 1.38x 랜덤 컴포넌트 배열 순회 89.5480ms 9.49x 랜덤 엔티티 배열 순회 149.9980ms 15.90x 처음 방법이 DOD 방식이고 맨 아래가 일부러 캐시 미스를 발생시킬 때 속도 차이이다. 위의 표는 한 가지 타입의 컴포넌트로 테스트 했고 네 가지 타입으로 테스트하면 최대 50 배 정도의 성능 차이가 발생했다.
  • 24. DOD 적용 사례 Parallelizing Conqueror's Blade Culling the Battlefield : Data Oriented Design in Practice Data-Oriented Design and C++
  • 25. 저거 다 설명하려면 일주일 내내 발표해야 함. 대충 유니티만 알아봅시다.
  • 26. 얘들아 나 유니티 코펜하겐 갔다왔어. 거기서 DOTS 발표하더라올ㅋ 그래봤자 유니티… 니들은 언리얼 성님 못따라 간다 - 평화로운 스터디 중 어느 날 -
  • 27. - 코로나 사태로 쉬던 중 - 아 심심해 뭐할꺼 없나… 그때 알콜코더님이 말씀하신 발표나 볼까? 이건 발표 해야만 해 헐?! 이럴수가
  • 28. Unity DOTS • Data-Oriented Tech Stack 그냥 DOD라 하면 없어 보이잖아? • 핵심 기능 ECS Model Job System Burst Compiler
  • 29. ECS Model • Entity Component System Entity = ID Component = Data Component System = Behavior Entity A Entity B Entity C System Translation Rotation LocalToWorld Renderer Translation Rotation LocalToWorld Renderer Translation Rotation LocalToWorld L2W = T * R
  • 30. ECS Model AI Data Rendering Data Physics Data Rendering Data Rendering Data Rendering Data AI Data Physics Data AI Data Physics Data AI Data Physics Data Entity는 개념적으로만 컴포넌트를 소유하고 실제로는 ID만 가지고 있다. Entity 0 Entity 1 Entity 2 Entity 3 AI Component Array Physics Component Array Render Component Array Update AI Data AI Component System Update Physics Data Physics Component System Update Rendering Data Rendering Component System
  • 31. Entity Entity 실제 구현부를 보면 Index(ID)와 Version 관련 변수를 소유하고 있을 뿐, Component를 직접적으로 소유하고 있지 않다.
  • 32. Archetype N Archetype • 컴포넌트의 조합에 따라 Archetype으로 분류된다. Entity A, B는 Archetype M인 컴포넌트 조합을 소유한 반면, Entity C는 Archetype N인 컴포넌트 조합을 소유한다. Entity A Entity B Entity C Translation Rotation LocalToWorld Renderer Translation Rotation LocalToWorld Renderer Translation Rotation LocalToWorld Archetype M
  • 33. Memory Chunks • 컴포넌트 배열은 Archetype 별로 생성된다. • 하나의 배열에 모든 컴포넌트를 담지 않고 16K 바이트로 쪼개어 담는다. ArcheType 16K Chunk Position Array Rotation Array Renderer Array 16K Chunk Position Array Rotation Array Renderer Array 16K Chunk Position Array Rotation Array Renderer Array ArcheType 16K Chunk Position Array Renderer Array 16K Chunk Position Array Renderer Array 16K Chunk Position Array Renderer Array
  • 34. World • World는 하나의 EntityManager 객체와 System Group을 포함하는 개념이다. World Entity Manager System Group World Entity Manager System Group World Entity Manager System Group
  • 35. Job System • 사용자가 쓰기 쉽도록 구현된 멀티쓰레딩 라이브러리 Parallelizing the Naughty Dog Engine Using Fibers(2015) - GDC Vault
  • 36. Job System : Terms Job : 실행할 작업 Worker Thread Fiber : Job을 실행하기 위한 Context ( User provided stack, register) Worker Thread Worker Thread : Job을 실행할 유닛 Worker Thread
  • 37. Job System : Execute Job CPU core core core core Worker Thread Worker Thread Worker Thread Fiber Pool Job Queue ※Worker Thread는 코어에 고정됨 ( 쓰레드 간의 컨텍스트 스위칭이 발생하지 않는다.) Job 실행하기 위해 먼저 Fiber Pool에서 Fiber 하나를 Worker Thread에 가져온다.
  • 38. CPU core core core core Worker Thread Worker Thread Worker Thread Job Queue 아직 Job이 없는 Worker Thread의 Fiber에 Job을 삽입한다. (Job은 Fiber 안에서만 실행한다.)
  • 39. CPU core core core core Worker Thread Worker Thread Worker Thread Job Queue Job은 실행 중 새로운 잡을 생성할 수도 있다.
  • 40. Job System : Synchronization CPU core core core core Worker Thread Worker Thread Worker Thread Wait List 1 실행 중 대기 상태가 된 Job은 Wait List에 옮겨져 Wait List의 카운터가 갱신될 때까지 대기한다. (Fiber와 함께 옮겨져 실행에 필요한 Context은 보존된다.)
  • 41. CPU core core core core Worker Thread Wait List 1 Worker Thread에는 새로운 Fiber가 들어가 다른 Job을 실행한다. Fiber Pool Job Queue
  • 42. Worker Thread Wait List 0 대기 상태의 잡과 동기화에 관련된 잡이 완료되면, 대기 리스트의 카운터를 0으로 만들고 파이버는 풀로 반환된다. Fiber Pool
  • 43. CPU core core core core Worker Thread Wait List 0 대기 상태였던 Job은 다시 옮겨져 Worker Thread에서 실행된다. Job System에서 유일한 동기화 방법이다.
  • 45. Race Condition 0Thread A + 1 0 + 1Thread B 0Shared variable 1 1 read read write write 메인 쓰레드의 데이터에 대한 레퍼런스를 Thread A, B가 동시에 접근하는 경우 경쟁 상태가 발생한다. non-mutually exclusive operations cause error interrupted read/write
  • 46. Job Safety Unity DOTS : Job System 0 + 1Thread A 1 + 1Thread B Data (In) 1 read read writewrite operations mutually exclusive, non-interrupted Shared Data (Out) 1 0 2 Schedule dependency Blittable NativeContainers data copy data copy
  • 47. Job Safety • 잡 시스템은 메인 스레드의 데이터에 대한 레퍼런스를 잡에 보내지 않고, 데이터의 복 사본을 보낸다. • 이 복사본은 해당 잡 안에서만 사용하므로 경쟁 상태가 발생하지 않는다. • 잡은 NativeContainer에 결과를 저장하여 데이터를 공유할 수 있다. • 잡 간의 실행 순서는 실행 예약(schedule)을 통해 정한다. (Schedule dependency)
  • 48. Burst Compiler • 최적화된 Native code를 생성해주는 컴파일러 • 모노 컴파일러로 컴파일한 코드는 C++에 비해 최대 10~30배 정도 느리다. • 버스트 컴파일러는 IL 코드를 C++로 만들고 이를 Native code로 변환한다. Behind the Burst Compiler
  • 49. Burst Compiler의 제약 • Job System 내에서만 사용할 수 있다. • Managed 객체(C# 클래스)를 사용할 수 없다.
  • 50. 기존 유니티 방식 • MonoBehavior 기반 클래스에 데이터와 행동을 정의 MonoBehaviour • Data • Behavior MonoBehaviour • Data • Behavior MonoBehaviour • Data • Behavior MonoBehaviour • Data • Behavior
  • 51. Pure ECS • 순수 ECS는 ComponentData 구조체에 데이터를 정의하고 시스템에서 행동을 정의 IComponentData • Data IComponentData • Data IComponentData • Data IComponentData • Data ComponentSytem • Behaviour
  • 52. Hybrid ECS • Hybrid ECS는 MonoBehavior 기반 클래스에 데이터만 정의하고 시스템에서 행동을 정의 MonoBehaviour • Data MonoBehaviour • Data MonoBehaviour • Data MonoBehaviour • Data ComponentSytem • Behaviour
  • 53. Component System 실행 순서 • Component System은 Initialization, Simulation, Presentation 단계 중 어디에 위치할 지 선택할 수 있다. (Default : Simulation) 각 단계 별로 System Group을 소유하고 있으며, 개발자가 정의한 System의 실행 순서는 원하는 대로 지정할 수 있다. 그러면 그에 맞게 Component System의 OnUpdate 함수가 호출 된다. Initialization Simulation Presentation
  • 54. Component System 실행 순서 참고로 Monobehavior 객체의 Update 함수 호출 시기는 위와 같다. Initialization Simulation Presentation FixedUpdate() Update() LateUpdate()
  • 55. 정리 • Data Oriented-Design은 현대 하드웨어에 친화적인 디자인 패턴 • 유니티는 DOD를 적용하기 위해 Dots 도입 ECS Jobsystem Burst Compiler • 아직은 과도기이고 적용 사례가 빈약하지만 향후 기대
  • 56. ECS Example 큐브를 만들어 Rotator라는 스크립트를 추가하고 코드를 연다.
  • 57. 열어보면 다음과 같은 코드가 보인다.
  • 58. 시간에 따라 회전하도록 만들려면 기존에는 위와 같이 코드를 작성한다.
  • 59. 이제 ECS 방식으로 만들기 위해 Data와 Behaviour를 분리한다.
  • 60. Rotator와 Transform 컴포넌트를 가진 엔티티를 불러와 회전시킨다.
  • 62. 그러나 실행해보면 큐브는 회전하지 않는다. 그 이유는 큐브가 엔티티가 아닌 게임 오브젝트이기 때문이다. 따라서 큐브에 Convert To Entity 컴포넌트를 추가해 주면 이 문제가 쉽게 해결된다.
  • 63. Job System : Define Job Job은 구조체로 IJob을 상속 받아 정의한다.
  • 64. Execute 함수의 for문은 쓰레드 하나에서 모두 처리된다.
  • 65. IJobParallelFor를 이용하면 For문이 병렬화 되어 모든 Worker 쓰레드로 분할되어 처리된다.
  • 66. 게임 오브젝트의 Transform을 변환하고 싶다면 IJobParallelForTransform을 이용한다.
  • 67. Job System : Create Job
  • 68. Start Job Job의 실행을 예약하려면 위와 같이 선언하면 된다. 스케줄 함수의 매개변수로 잡핸들을 넣으면 그 핸들의 잡이 처리된 후 시작하도록 예약한다. - 스케줄 종속성(Schedule dependency) -
  • 69. JobParallelFor의 핸들의 스케줄 함수는 조금 더 복잡하다. 첫번째 매개변수는 For의 반복 횟수를 의미하고, 두번째는 각 Worker Thread당 몇 개의 잡을 처리할지를 의미한다.
  • 70. Transform 객체의 managed array를 생성하고 TransformAccessArray에 랩핑한다. IJobParallelForTransform 기반 객체는 이 랩핑한 배열을 schedule의 매개변수로 받아와 처리한다.
  • 71. 모든 잡에 대한 스케줄을 짰으면 이를 잡시스템에 알린다. 그러면 잡시스템은 잡을 처리한다. Schedule과 ScheduleBatchedJobs를 이용하지 않고 잡 객체의 Run 함수를 이용하여 잡을 처리할 수도 있다.
  • 73. Job System : Example