【问题标题】:How does cache works when accessing multiple memory locations in a loop?在循环中访问多个内存位置时,缓存如何工作?
【发布时间】:2016-01-29 09:29:27
【问题描述】:

我目前正在研究面向数据的设计/实体-组件-系统,并考虑将其应用于某些算法,如果不是整个程序的话。

在低层次上,我了解这种设计如何允许编写通过缓存使用高效内存访问的代码。

我见过很多这样的例子:

// position is a (x,y) coordinate pair

for ( int i=0; i!=position_count; ++i ) {
  do_something_with(position[i]);
}

我明白了。我们不是遍历所有“胖”对象(带有 x,y,state,name,hp 等...),而是遍历该对象的一小部分(这里是单个位置 x,y)。 这些位置包含在一个连续的内存位置(一个数组)中。代码看起来很高效:数据很少,所有内容都在一个循环中读取,允许 L1 缓存完成其工作。

现在理论上这看起来很完美,但在一个真实世界的例子中,我想知道它是如何工作的。

在这个例子中:

// visual is a (x,y) coordinate pair
// and also contain an index to the sprite array

for ( int i=0; i!=visual_count; ++i ) {
      sprite s = sprites[visual[i].sprite_id];
      s.draw_at( visual[i].x, visual[i].y );
}

在这里,我们使用读取的数据来做一些有用的事情。 但这会导致读取第二个内存位置(精灵)。

Sprite 可能包含比一对坐标(宽度、高度、纹理位置等)更多的数据。 此外,精灵数组很可能是相当随机的访问。

它不会“破坏”之前遍历位置的缓存吗? L1、L2、L3 缓存有哪些内容? 在这种情况下编写高效代码的最佳方法是什么?

【问题讨论】:

    标签: caching memory optimization


    【解决方案1】:

    一般的答案是“视情况而定”。在某种程度上,您不应该在预优化上投入太多精力,确保在某些情况下您确实需要/想要,然后您需要针对您正在优化的特定系统,以了解它可能会对其他系统的性能产生负面影响。

    理论上,L1 是最近且最快的 ram,最昂贵且尺寸最小。用于确定访问如何存储在 L1 中的算法/逻辑确实是这里的问题。有可能知道它是如何工作的(如果你没有关于逻辑如何工作的信息,你可能必须通过实验来做)来设置你的内存地址空间,这样在同一个数组或不同数组中的多个项目不会发生冲突缓存。很有可能,尤其是如果您选择相距很远的对齐地址空间(许多/大多数低地址位匹配,只有高地址位不同),它们将命中缓存中的相同空间而使其他部分未使用。

    您还需要知道缓存是在虚拟地址还是物理地址上运行。无论如何,通常虚拟和物理的低位匹配,但仍然可以是缓存使用的地址位。

    L2 包含 L1 以及更多内容。 L3 包含所有 L2 的内容以及更多内容。每当 L1 错过它然后通过 L2 获取,然后它要么命中 l2(已经存在)要么错过 L2,然后通过 L3 读取它命中或未命中的地方,使 L1 中的任何内容都在 L2 和 L3 中,而 L2 中的任何内容都在 L3 中.

    【讨论】:

    • 如果你的循环足够大,很有可能循环数据本身大于 L1 的大小并且会发生碰撞和驱逐,但希望都适合 L2。不像 L1 那样接近和快,但仍然比 L2 的另一边更快(如果你甚至有 L2)。
    猜你喜欢
    • 2020-09-08
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    • 2012-08-09
    • 2021-05-01
    • 2013-12-26
    • 1970-01-01
    • 2021-04-22
    相关资源
    最近更新 更多