Caching 当访问循环中的多个内存位置时,缓存是如何工作的?

Caching 当访问循环中的多个内存位置时,缓存是如何工作的?,caching,memory,optimization,Caching,Memory,Optimization,我目前正在研究面向数据的设计/实体组件系统,并考虑将其应用于一些算法,如果不是整个程序的话 在一个较低的层次上,我理解这种设计如何允许编写通过缓存使用高效内存访问的代码 我见过很多这样的例子: // position is a (x,y) coordinate pair for ( int i=0; i!=position_count; ++i ) { do_something_with(position[i]); } 我明白了。我们不是遍历所有“胖”对象(带有x,y,state,nam

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

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

我见过很多这样的例子:

// 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)。 这些位置包含在连续的内存位置(数组)中。代码似乎很有效:数据量很小,所有内容都在一个循环中读取,允许一级缓存完成其工作

现在在理论上这似乎是完美的,但在现实世界的例子中,我想知道它是如何工作的

在本例中:

// 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)

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

它不是“破坏”了之前迭代位置的缓存吗? 一级缓存、二级缓存和三级缓存中有哪些内容? 在这种情况下,编写高效代码的最佳方法是什么?

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

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

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


L2包含L1和更多内容。L3保存了所有的L2内容,还有更多。当L1未命中时,它会通过L2获取,然后它会命中L2(已经存在)或未命中L2,然后通过L3读取它命中或未命中的内容,L1中的任何内容都在L2和L3中,L2中的任何内容都在L3中。

如果您的循环足够大,则很可能循环数据本身大于L1大小,并发生碰撞和逐出,但希望所有内容都适合L2。没有L1那么近,也没有L1那么快,但仍然比L2的另一边快(如果你有L2的话)。