设定
我有一个实体组件体系结构,其中实体可以具有一组属性(它们是纯数据,没有任何行为),并且存在运行运行于该数据上的实体逻辑的系统。本质上,用某种伪代码:
Entity
{
id;
map<id_type, Attribute> attributes;
}
System
{
update();
vector<Entity> entities;
}
只是以恒定的速率沿所有实体移动的系统可能是
MovementSystem extends System
{
update()
{
for each entity in entities
position = entity.attributes["position"];
position += vec3(1,1,1);
}
}
本质上,我正在尝试尽可能高效地并行化update()。这可以通过并行运行整个系统来完成,也可以通过为一个系统的每个update()提供几个组件来实现,以便不同的线程可以执行同一系统的更新,但对于在该系统中注册的实体的不同子集而言。
问题
对于所示的MovementSystem,并行化是微不足道的。由于实体彼此不依赖,并且不修改共享数据,因此我们可以并行移动所有实体。
但是,这些系统有时要求实体彼此交互(从/向彼此读/写数据),有时在同一系统内,但经常在彼此依赖的不同系统之间。
例如,在物理系统中,有时实体可能会彼此交互。两个对象发生碰撞,它们的位置,速度和其他属性会从它们读取,更新,然后将更新后的属性写回到两个实体。
并且在引擎中的渲染系统可以开始渲染实体之前,它必须等待其他系统完成执行,以确保所有相关属性都是它们所需要的。
如果我们试图盲目地并行化它,这将导致经典的竞争条件,其中不同的系统可能同时读取和修改数据。
理想情况下,将存在一种解决方案,其中所有系统都可以从其希望的任何实体读取数据,而不必担心其他系统同时修改相同的数据,而无需程序员关心正确地排序执行和并行化。这些系统是手动的(有时甚至是不可能的)。
在基本实现中,这可以通过将所有数据读写放在关键部分中(用互斥锁保护它们)来实现。但是,这会导致大量的运行时开销,并且可能不适用于对性能敏感的应用程序。
解?
在我看来,可能的解决方案是将数据的读取/更新和写入分开的系统,以便在一个昂贵的阶段,系统仅读取数据并计算其需要计算的内容,以某种方式缓存结果,然后将所有内容写入将更改后的数据通过单独的写入传递回目标实体。所有系统都将以数据在帧开始时的状态对数据进行操作,然后在帧结束之前,在所有系统完成更新后,会发生序列化写入过程,其中所有不同的缓存结果系统遍历并写回到目标实体。
这是基于(可能错了?)的思想,即简单的并行化胜利可能足以超过结果缓存和写入过程的成本(在运行时性能以及代码开销方面)。
问题
如何实现这样的系统以获得最佳性能?这样的系统的实现细节是什么?要使用此解决方案的实体组件系统的先决条件是什么?