为什么缓冲区应该在 64 字节边界上对齐以获得最佳性能?

why buffers should be aligned on 64-byte boundary for best performance?

this 示例程序中我发现了这条注释:

/* Hardware delivers at most ef_vi_receive_buffer_len() bytes to each
 * buffer (default 1792), and for best performance buffers should be
 * aligned on a 64-byte boundary.  Also, RX DMA will not cross a 4K
 * boundary.  The I/O address space may be discontiguous at 4K boundaries.
 * So easiest thing to do is to make buffers always be 2K in size.
 */
#define PKT_BUF_SIZE         2048

我很感兴趣,为什么 for best performance buffers should be aligned on a 64-byte boundary?例如,为什么 2000 个缓冲区比 2048 个缓冲区慢?我想这就是 64 位计算机的工作方式 - 由于某种原因,memcpy 2048 字节比 2000 字节更快?

为什么恰好 2048 个缓冲区更快,也许您可​​以 link "minimal example" 其中 "bigger but 64-byte aligned" 个缓冲区更快?

64 字节是现代架构中 缓存行 的流行大小。任何从内存中获取的内容都会获取整个缓存行。通过将数据与缓存行边界对齐,可以最大限度地减少读取数据时需要获取的缓存行数以及写入数据时变脏的缓存行数。

当然,数据的大小也很重要。例如,如果数据的大小除以缓存行的大小,那么只对齐大小就完全没问题了。

相比之下,假设您的数据有 96 个字节。如果按 32 字节对齐,则最多可以使用三个缓存行:

|............DDDD|DDDDDDDDDDDDDDDD|DDDD............|

相比之下,如果你对齐 64 字节(需要另外 32 字节的填充),你只需要两个缓存行:

|................|DDDDDDDDDDDDDDDD|DDDDDDDDPPPPPPPP|

(D = data, P = padding, 每个字符代表4个字节。)

当您同时修改内存时,缓存行甚至更值得关注。每次弄脏一个缓存行时,所有其他已获取同一缓存行的 CPU 可能不得不丢弃并重新获取这些缓存行。不小心将不相关的共享数据放在同一缓存行上称为 "false sharing",插入填充通常用于避免这种情况。

简短的回答是,大多数当代 x64 处理器上的数据缓存行是 64 字节宽,因此 CPU 从主内存中执行的每次提取都是一次 64 字节。如果您要加载跨越 64 字节边界的 64 字节结构,则 CPU 必须获取两个缓存行才能获取整个结构。

真正的答案是这个主题太复杂,无法放入答案框,但 Ulrich Drepper's excellent "What Every Programmer Should Know About Memory" paper 会给您一个完整的解释。

另请注意,64 字节不是计算的基本定律,也与 64 位处理器无关。它恰好是当今大多数工作站中 x64 处理器上最常见的缓存行大小。其他处理器具有不同的缓存行大小(例如,Xbox360 和 PS3 中使用的 Xenon PowerPC 具有 128 字节缓存行)。