ecs性能核心是缓存友好:组件须连续存储于独立数组,实体id为索引+版本号,系统显式声明读写组件类型,禁用rtti、虚函数、事件总线及std::vector动态分配。

组件数据必须连续存储在数组里,不能用 std::vector<:unique_ptr>></:unique_ptr>
面向数据的ECS核心是缓存友好——CPU一次预取几十字节,如果组件指针乱跳,性能直接崩。用 std::vector<:unique_ptr>></:unique_ptr> 存组件,等于把每个实例扔到堆上随机地址,for 遍历时全是缓存未命中。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每种组件类型单独一个
std::vector(如std::vector<transform></transform>、std::vector<health></health>),内存天然连续 - 实体ID只是数组下标(或带版本号的索引),避免指针或句柄间接寻址
- 组件增删不调用
push_back/erase,改用“交换并弹出”(swap-and-pop)维持连续性,否则会破坏遍历局部性 - 如果需要运行时动态组件类型,用
std::array<:byte n></:byte>+ placement new 手动管理内存,别碰std::any或虚函数表
系统(System)必须显式声明读写哪些组件类型,不能靠 RTTI 或反射自动推导
自动扫描组件类型听着省事,但会锁死编译期优化:编译器看不到访问模式,无法向量化、无法内联、无法重排指令。更糟的是,RTTI 在嵌入式或 console 平台常被禁用。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每个系统类用模板参数或静态成员变量明确列出依赖,例如
struct RenderSystem { static constexpr auto reads = std::tuple<transform mesh>; };</transform> - 调度器根据这些声明,在运行时按组件数组对齐程度、大小、访问频率做批处理(如把所有只读
Transform的系统合并在一次遍历中跑完) - 避免在系统内部用
dynamic_cast或type_info::name()做运行时分发——这已经不是面向数据,是面向对象的残余 - 如果要用脚本扩展系统逻辑,导出的是纯数据(如 JSON 描述“对所有有 A 和 B 的实体执行 C 操作”),而非裸指针或对象引用
实体ID不能是裸整数,但也不该是带引用计数的智能指柄
裸整数(如 int32_t)看着快,但删实体后重用 ID 会导致悬空访问;而 std::shared_ptr<entity></entity> 这类句柄又引入原子操作和堆分配,违背 ECS 的零成本抽象原则。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用“索引+版本号”结构体,例如
struct EntityID { uint32_t index; uint16_t version; };,版本号在实体回收时递增 - 实体池(
EntityPool)用std::vector<entityslot></entityslot>,其中EntitySlot包含bool alive+uint16_t version,查表 O(1),无分支预测失败风险 - 禁止把
EntityID存进组件数据结构里——组件里只存索引(uint32_t),版本校验由系统层统一做,避免重复开销 - 调试时可加
#ifdef DEBUG断言检查版本号,发布版直接删掉,不污染热路径
不要在C++里实现通用的“事件总线”或“消息队列”来解耦系统
事件驱动听着松耦合,实际让数据流不可预测:谁发、谁收、何时收、是否丢弃,全在运行时决定。CPU 流水线清空、缓存行反复加载、内存屏障乱飞——ECS 的确定性就毁在这儿。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 系统间通信只通过共享组件数据(如
CommandBuffer组件存一串待执行动作,InputSystem写,ActionSystem读) - 帧同步点强制刷新:每帧末尾统一处理“延迟命令”,避免跨帧状态不一致,也方便调试时断点拦截
- 如果真要异步(如加载资源),走独立线程+无锁队列(
moodycamel::ConcurrentQueue),但队列内容只能是 POD 数据,不能传std::function或捕获 lambda - 编辑器热重载支持?用文件监听 + 内存映射重载组件定义,而不是在运行时 patch 虚函数表
最难的不是写对,是忍住不用熟悉的东西——比如把 std::map 当成万能容器,或者给每个组件加个 virtual void update()。数据布局一旦定型,后期改 cache line 对齐或 prefetch 指令都得翻车。











