前进!前进!不择手段的前进!
一、ECS是什么
Entity(实体) Component(组件) System(系统)
是一种基于数据的设计(DOD Data-Oriented Design)dodbook
ECS的泛泛之谈
深入浅出Unity ECS
ECS 真的是「未来主流」的架构吗?
面向对象编程的弊端是什么?
面向数据编程
- 实体:持有全局唯一标记
- 组件:仅持有数据 纯数据
- 系统:仅持有逻辑 纯函数
for (s in systems) {
s.doSomething(data); // data为对应系统需要使用的数据
}
Q:什么是逻辑帧?
A:任何一个持续运行的程序,内部都有一个死循环。每执行一次循环,称为一个逻辑帧
二、几种设计流派的理解
OOD(Object-Oriented Design)
- 总结共同数据与逻辑,抽象到基类中
COD(Compoment-Oriented Design)
- 打包数据与逻辑,抽象成一个个的组件对象
- 基于组件
- 仅抽象出共有逻辑,规范行为
DOD(Data-Oriented Design)
- 以COD为基础,再把数据、逻辑分离
三、流派总结
实际上基于组件与基于接口本质相同,都是为了解决传统基于对象导致基类过重的问题。
基于组件、基于接口也得到了广泛的应用。
基于对象设计:基于现实的抽象。对象持有自己的所有数据、行为。
- 优点:容易理解。基于接口的设计方式已经足够好。行为的统一、类之间的解耦都很好。
- 缺点:性能上限低。面向对象编程的弊端是什么?
基于数据设计:行为是行为,数据是数据。行为只处理数据。行为本身不属于某个具体的对象。
- 优点:可以做到极致的性能 面向数据编程
- 缺点:难理解,高性能的工程实现很难
做ECS是为了优化性能,因为接口编程已经足够解耦了。
四、基于数据的设计,为什么可以做到高性能?
先看下传统写法为什么低效
for (p in players) {
if (condition) {
p.doSth();
}
}
- 数据内存上离散,缓存不友好
- 分支预测不友好
- 虚函数调用开销大
五、CPU缓存友好
CPU读取内存比较慢,所以设计了一套缓存
一次读取一个cache line长度的数据
缓存三要素
- 在相对较慢的交互之间插入一个高速的缓存层
- 缓存层需要有数据卸载机制
- 预测式加载数据
广度:
- 服务器的缓存技术
- 客户端的缓存技术
- 还有没有其他的?
深度:
缓存技术遇到的问题
- 客户端需要考虑上边几个吗? 可靠、不可靠调用
- 多核CPU怎么保证缓存一致性的? 数据一致性
六、分支预测
处理器猜测进入哪个分支,并且基于预测结果来取指、译码。
如果猜测正确,就能节省时间。
如果猜测错误,刷新流水线,在新的地址处取指、译码。
广度:
- CPU还有啥骚操作?
- 浅谈运行期间cpu指令重排
- 其他地方有没有骚操作?编译器,虚拟机?
深度
- 指令重排、缓存一致性。都会影响到多线程开发
- 进程、线程、协程适用范围
- go强在哪?
- 协程能完全取代线程?
- 浅谈利用分支预测提高效率
- 一文告诉你CPU分支预测对性能影响有多大
七、虚函数调用开销
对每一个虚函数的调用都需要额外的指针寻址
虚函数通常不能被inline,当虚函数都是小函数时会有比较大的性能损失
每个对象都需要有一个额外的指针指向虚表
八、以上三个问题,需要怎么解决?
缓存友好
- 语言需要支持POD类型
需要三个基本池
- 内存池 – 减少系统调用
- 线程池 – 真正的并行
- 对象池 – 数据连续存储的保证
分支预测
- 任何语言都可以
虚函数调用开销
- 需要支持简单函数的定义
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。