【问题标题】:Data oriented design in OOPOOP 中的面向数据设计
【发布时间】:2012-08-21 22:05:00
【问题描述】:

In this slides (在幻灯片 15 之后)建议使用

void updateAims(float* aimDir, const AimingData* aim, vec3 target, uint count)
{
     for(uint i = 0; i < count; i++)
     {
          aimDir[i] = dot3(aim->positions[i], target) * aim->mod[i];
     }
}

因为它的缓存效率更高。

如果我有课怎么办

class Bot
{
    vec3 position;
    float mod;
    float aimDir;

    void UpdateAim(vec3 target)
    {
         aimDir = dot3(position, target) * mod;
    }
 };

 void updateBots(Bots* pBots, uint count, vec3 target)
 {
      for(uint i = 0; i < count; i++)
            pBots[i]->UpdateAim(target);
  }

我将该类的所有对象存储在一个线性数组中。

由于它们都在同一个数组中,是否会有缓存未命中? 为什么第一种方法会更好?

【问题讨论】:

    标签: c++ data-oriented-design


    【解决方案1】:

    现代缓存架构通常被构造为数据,每行大到足以容纳几个字; 64 字节是典型的行大小。当您尝试读取不在缓存中的数据时,将获取整行,而不仅仅是您需要的单词。写入时,如果缓存中的数据存在则更新,但如果不存在则通常不需要获取。

    在第一种情况下,对于获取的输入数据的每个缓存行,您将使用其中的每个单词。在第二个中,您将只使用一些结构字段;获取其他人浪费了一些带宽。

    具体来说,您每次都获取aimDir 的旧值,计算不需要它。一般来说,“对象”可能包含更多字段,您不希望这些字段用于此特定计算,在将它们提取到缓存中然后被忽略时浪费更多带宽。

    【讨论】:

    • 即使计算不需要aimDir,难道不需要存储结果吗?或者缓存对于只写变量的工作方式是否不同?
    • @TiagoCosta:确实,缓存在写入时表现不同。数据无需在缓存中即可写入。
    • 因此数据导向应该会产生显着的性能改进。当在任意位置使用动态分配对象的多态性时,预取器根本无法做任何事情,并且缓存可能会被不需要的数据填充。但是,如果不是对象存储自己的数据,而是使用连续容器来顺序存储数据,预取就可以发挥作用,并且缓存中的垃圾数量将最少。
    【解决方案2】:

    内存布局差异很大,如果使用 Bots 数组,第一种方法的好处将被破坏。

    在第一种方法中,所有aimDir 数据都存储在非分段的内存块中。因此,如果您处理了第一项,则可以立即继续处理下一项,因为它存储在下一个内存单元中。

    如果您有一个Bots 数组,那么您有Bot 对象存储在非分段内存块中。但是两个机器人的不同aimDir数据现在被机器人的其他数据分开(positionmod)。

    从图形上看,第一种方法(如果也假设位置和模的数组)看起来像

    [R] 表示与机器人无关的随机未知数据

    [R][position_0][position_1]...[position_n][R][mod_0][mod_1]...[mod_n][R][aimDir_0][aimDir_1]...[aimDir_n][R]
    

    第二种方法如下所示:

    [R][[position_0],[mod_0],[aimDir_0]][[position_1][mod_1][aimDir_1]]...[[position_n][mod_n][aimDir_n]][R]
    

    【讨论】:

      猜你喜欢
      • 2012-03-27
      • 2023-03-20
      • 1970-01-01
      • 2014-02-19
      • 2013-10-23
      • 1970-01-01
      • 2010-12-11
      • 2013-04-15
      • 1970-01-01
      相关资源
      最近更新 更多