24

设计 C++ 程序时如何减少可能的缓存未命中次数?

内联函数每次都有帮助吗?还是只有在程序受 CPU 限制时才好(即程序是面向计算而不是面向 I/O)?

4

6 回答 6

35

以下是我在处理此类代码时喜欢考虑的一些事情。

  • 考虑您是否想要“数组结构”或“结构数组”。您要使用哪个取决于数据的每个部分。
  • 尝试将结构保持为 32 字节的倍数,以便它们均匀地打包缓存行。
  • 将数据划分为冷热元素。如果您有一个 o 类对象数组,并且您经常一起使用 ox、oy、oz,但只是偶尔需要访问 oi、oj、ok,那么请考虑将 ox、oy 和 oz 放在一起并移动 i、j 和k 部分到一个并行的辅助数据结构。
  • 如果您有多维数据数组,那么使用通常的行顺序布局,沿着首选维度扫描时访问将非常快,而沿着其他维度扫描则非常慢。沿着空间填充 曲线映射它将有助于平衡在任何维度中遍历时的访问速度。(阻塞技术是类似的——它们只是具有较大基数的 Z 顺序。)
  • 如果您必须招致缓存未命中,请尝试尽可能多地使用该数据以分摊成本。
  • 你在做多线程的事情吗?注意缓存一致性协议的减速。填充标志和小计数器,以便它们位于单独的缓存行上。
  • 如果您提前知道要访问的内容,Intel 上的 SSE 会提供一些预取内在函数。
于 2009-01-20T10:49:24.987 回答
10

对于数据绑定操作

  1. 在列表、地图和集合上使用数组和向量

  2. 按行处理列

于 2009-01-20T10:16:05.553 回答
2

允许 CPU 有效地预取数据。例如,您可以减少按行而不是按列处理多维数组的缓存未命中数、展开循环等。

这种优化取决于硬件架构,因此您最好使用某种特定于平台的分析器(例如 Intel VTune)来检测缓存可能存在的问题。

于 2009-01-20T10:09:46.457 回答
2

内联函数运行可能会损害指令缓存。如果内存不受 fetch 限制,那么它不太可能产生太大(如果有的话)差异。

与往常一样,任何优化都应该通过分析而不是预感来通知。更不用说您需要了解分析器告诉您的内容,这意味着您熟悉汇编语言以及您正在优化的平台的特定特征。

现在有点老了,但 Mike Abrash 的“Graphic's Programming Black Book”仍然有很多很好的一般性建议。

于 2009-01-20T10:47:13.227 回答
2

此外,如果您使用 C++ 和多线程,则需要考虑错误共享、局部性和每个处理器缓存上数据的热度。这可以产生很大的不同。尤其是在多线程中,以 LIFO 方式计算事物比以 FIFO 方式计算更有效,但它在单处理器架构中也有效。

于 2009-01-20T11:01:13.263 回答
1

避免在不需要时使用动态内存。使用 new、delete、smart pointers 等等,往往会将您的程序数据分散到内存中。这不好。如果您可以将大部分数据保存在一起(例如,通过在堆栈上声明对象),那么您的缓存肯定会工作得更好。

于 2009-01-20T10:43:13.393 回答