如果对象大小 > 缓存行,空间局部性对缓存性能是否重要?

ram*_*owl 5 c++ optimization cpu-cache

假设我正在存储一个对象的链接列表,每个对象都是一个大小为 64 字节的结构,这也是我的缓存行的大小。随着时间的推移,我将在链接列表上进行延迟敏感的添加、删除和迭代。据我所知,性能主要取决于对象是否保存在缓存中,因此 RAM 访问的访问量约为 1 纳秒,而不是 > 50 纳秒。

通常建议通过空间局部性来实现这一点,最好将对象存储在连续的内存块中。这很有用,因为每当我访问内存地址时,处理器实际上都会提取缓存行的数据;我们希望这些附加数据成为其他有用的对象,因此我们将所有对象放在一个连续的块中。

我可能会误解,但如果对象大小 >= 缓存行大小,我们似乎不会从这种效果中受益。一次只能将一个对象放入缓存中。

Var*_*rak 5

当数据大小小于缓存大小时,除了预加载后续项目的好处之外,要考虑的另一个因素是关联性和映射问题。在链表的情况下,您没有连贯的布局(或者至少不能保证这样),因此与数据以空间局部性布局相比,您更有可能遇到冲突。最重要的是,使用链表模型确实会面临内存碎片级别的风险,尽管我不确定这是否是您应该担心的事情。

根据使用情况、访问模式等,对于您正在做的事情,绝对值得权衡算法效率的相对优势(删除在链表中非常便宜,在数组或类似中昂贵)。如果您进行大量删除/插入操作,那么算法效率的好处可能远远超过缓存一致性的好处。

为了澄清关联性概念,值得一看。基本上,缓存的关联性决定了特定地址可以映射到缓存中的多少个位置。不同的缓存级别将具有不同的关联性,因此例如在大多数情况下,L1 缓存是 2 路或 4 路组关联的,这意味着任何地址都可以映射到缓存中的两个(或四个)位置之一。L2 和 L3 更可能是 8 路(有时是 12 路或 16 路)关联。

就 Intel/AMD/等 CPU 的特定关联性而言,这是一个更艰难的决定,因为即使是 Intel 人员也很难想出一个好的答案!我发现的一个例子是 Xeon x5660 CPU,它在 L1 中是 4 路组关联指令,在 L2 中是 8 路组关联数据,在 L2 中是 8 路组关联,在 L3 中是 16 路组关联。

现代 CPU 用于缓存使用等的算法非常令人惊奇,并且超出了此处概述的基础知识,因此我认为在实践中您会发现它的影响很小。