Unity DOTS学习记录(四)-内存模型
Chunk
在DOTS中,ECS会将实体数据组织在固定大小的内存块中,每个内存块大小为16KB,称为一个Chunk。Chunk是DOTS内存管理和调度的基本单元。
一个Chunk的整体结构如下:1
2
3
4
5┌──────────────────────────────┐
│ Chunk Header │ ← 元数据
├──────────────────────────────┤
│ Component Data (SoA) │ ← 实体数据
└──────────────────────────────┘
Chunk Header(组件数据)
Chunk Header中存放的是描述这块Chunk如何被ECS管理和解释的元数据,主要包括:
- Archetype(架构):组件类型,组件大小,每个组件数组在Chunk中的偏移(*)
- EntityCount:实体数量(int)
- ChangeVersion:每个组件的修改版本(uint[])
Chunk Header的大小通常在几百字节以内,占整个Chunk的比例很小,绝大多数空间用于存放组件数据。
Component Data
1 | public struct RotationSpeed : IComponentData |
以组件RotationSpeed为例,包含这个组件的Entity在Chunk中的实际内存布局如下:1
2
3
4
5
6
7
8
9
10
11
12
13Chunk (16 KB)
┌─────────────────────────────────┐
│ Chunk Header │
│ - Archetype │
│ - EntityCount │
│ - ChangeVersion │
├─────────────────────────────────┤
│ LocalTransform[] │
│ A[0] A[1] A[2] ... │
├─────────────────────────────────┤
│ RotationSpeed[] │
│ B[0] B[1] B[2] ... │
└─────────────────────────────────┘
高效的数据访问方式
1 | foreach (var (transform, speed) in |
在执行这个遍历时,ECS底层其实是将每个Chunk中的组件进行遍历:1
2
3
4for each Chunk:
for i in 0..EntityCount:
transform = LocalTransform[i]
speed = RotationSpeed[i]
这种连续存储的的方式可以提高Cache命中率,在连续访问中便于CPU预取。
JobEntity 与 Chunk 并行
当使用IJobEntity或ScheduleParallel()时:
- 不同Chunk之间可以并行处理
- 每个Job通常负责一个或多个Chunk
- 无需显式加锁,ECS已在Chunk层面保证安全
这也是ECS能在大量实体场景下保持高性能的关键原因。
总结
Chunk的设计决定了:ECS适合“小而多”的实体,不适合“大而少”的实体;当单个Entity接近Chunk大小时,ECS的性能优势将大幅减弱。