实体组件系统框架,CPU缓存友好

时间:2014-05-05 13:31:19

标签: data-modeling entity-system artemis

我找不到一个对CPU缓存友好的单一框架实现which means that data on which systems traverse in each game loop cycle is stored in a contiguous memory

让我们看看,系统遍历满足其条件的特定实体,即实体应包含要由X系统处理的A,B,C组件。这意味着我需要一个包含所有实体和组件的连续内存(不是引用,只要引用不是缓存友好的,并且你将有很多缓存未命中),以便尽可能快地从RAM中获取它们在X系统的处理过程中。但是在X系统处理之后,Y系统开始在满足其条件e的一组实体上运行。 g。所有包含A和B的实体。这意味着我们处理与X系统相同的一组实体以及一些具有A和B的其他实体。这意味着我们有两个具有重复数据的连续存储器。首先,由于已知原因,数据复制非常糟糕。而且,这反过来意味着我们需要一个同步,只要你需要从一个向量中找到一些实体并使用另一个向量中包含的新数据进行更新,这同样不是CPU缓存友好的。

这只是我的一个想法。对于实体组件系统框架数据模型还有其他更现实的想法,但在每个模型中我都可以发现存在同样的问题:在每个游戏循环周期中,由于不连续的数据,您无法阻止大量缓存未命中。 / p>

任何人都可以建议一个实现,文章,这个主题的例子,这可以帮助我理解应该使用什么数据模型来获得缓存友好设计,因为这是游戏性能中最关键的事情之一。

3 个答案:

答案 0 :(得分:7)

我会选择junkdog的答案(因为我写了链接的文章;)),但这是另一个,不同的,接受它:

如果您想要缓存友好型设计,则需要列出:

  1. 您的微处理器
  2. 您的处理器架构
  3. 您的巴士架构
  4. ...
  5. 您的每子框架工作集大小
  6. 所有游戏对象的总工作集/ RAM
  7. 特定游戏中的互连量
  8. ...... etc
  9. 根据这些要求的紧密程度或松散程度,您将对设计做出不同的简单(或硬性)决策。游戏开发人员经常重新编写内存管理。他们不这样做是因为他们是愚蠢的,他们这样做是因为每个项目(重新)优化每个项目很容易/值得(这是一个AAA标题?还是AA标题?图形更重要吗?还是网络延迟? ..等)和每个硬件(在PC上,目标硬件每个月都在变化)

    我建议你选择一套硬件,创建一个简单的基于ES的游戏,并开始尝试设计一个缓存友好的内存使用 - 并公开记录,使其全部开源,看看你是否可以获得其他对运行基准测试感兴趣的人。

答案 1 :(得分:4)

Adam Martin / t =机器最近发布了Data Structures for Entity Systems: Contiguous memory - 这是我唯一专门处理ECS内存布局的文章。

您没有指定语言,但在Java世界中,entreriartemis-odb(通过PackedComponents /也称为免责声明:我的端口)处理Adam所称的&#34 ;迭代1:每个ComponentType的BigArray"。

答案 2 :(得分:2)

从理论上讲,我认为这个问题需要付出太多努力来解决,以证明完美解决问题所需的时间。我过去已经花了太多时间在这上面,提出了复杂的解决方案,只能回到更简单的解决方案。我们最大的热点不一定来自实体/组件遍历的非强制缓存未命中。许多系统将为可以加速的特定实体做大量的工作,并且许多组件通常足够大以减少尝试以使多个邻居适合最小数量的方式对它们进行排序的好处。缓存行。

也就是说,如果您只是想要对组件进行排序,以便提供缓存友好的内存访问模式,而且仅针对一个或两个关键系统而不会出现重叠冲突,并且可能针对最小和最多的组件类型哪里一定会帮助最多,这很容易做到这里和那里的一些后处理。我建议您回答一下您的热点。

通常只是一些基本排序可以帮助您减少所有系统的缓存未命中份额,无论他们处理哪些组件组合。如果你从这样的代表(我使用的)开始:

enter image description here

经过一段时间运行游戏状态并偶尔删除和添加组件后,你最终会得到这样的结果:

enter image description here

你可以解开这些混乱并将其整理出来:

enter image description here

这可以通过基数排序非常便宜地完成,根据拥有它们的实体索引对元素进行排序作为线性时间的关键。通过一个不错的实现,您通常可以隐藏这一点,而不会注意到帧速率的任何打嗝。我以与上面的数据表不同的方式绘制图表(只是为了清楚哪个组件属于哪个实体),但是同样的想法。只需根据实体索引(实体ID)对组件数组进行基数排序,更新链接(使用并行数组在索引之前/之后映射,并使用实体索引作为键与组件数据一起排序),现在一切一切都很好,整洁,并没有与零星的访问模式纠缠在一起。

这可能不会让对系统特定组合感兴趣的系统成为一组完美连续的组件(可能会有一些间隙,如上图所示),但至少它不会来回往返在内存中,可能不得不将内存区域加载到缓存行中,只是为了逐出它然后再返回并重新加载它,并且在这些情况下很可能会连续访问许多组件。

如果这还不够好,那么给定具有特定实体的具体实体,系统对特定查询感兴趣的组件,您可以将组件排序到数组的顶部,对照系统需要的特定实体缩小任何间隙,现在您对系统具有完美的连续性,专门处理包含运动和渲染组件的实体。这也可以在线性时间内进行,后处理可以在这里和那里进行,也可以在删除并添加了许多组件后定期应用。

我从来没有发现过这么远的必要性。我现在只对实体ID进行广义排序,然后通常改进所有系统的访问模式(但没有针对任何给定系统的最佳解决方案)。您的用例可能需要最佳版本,但我建议您只关注具有大热点的关键系统,这些关键系统真正受益于它。