是否使用写合并缓冲区对Intel上的WB内存区域进行常规写操作?

Bee*_*ope 5 performance x86 intel cpu-architecture

写合并缓冲区一直是Intel CPU的功能,至少可以追溯到Pentium 4甚至更早。基本思想是这些高速缓存行大小的缓冲区将写操作收集到同一高速缓存行中,因此可以将它们作为一个单元进行处理。作为它们对软件性能的影响的一个示例,如果您不编写完整的缓存行,则可能会遇到性能下降的情况

例如,在《Intel 64和IA-32体系结构优化参考手册》中,“ 3.6.10写合并”部分以以下说明(加了重点)开头:

写合并(WC)通过两种方式提高性能:

•在对第一级缓存的写入未命中时,它允许在从缓存/内存层次结构的更深层读取所有权(RFO)之前对该同一个缓存行进行多个存储。然后读取剩余的行,将尚未写入的字节与返回行中的未修改字节合并。

•写合并允许将多个写组合在一起,并作为一个单元在高速缓存层次结构中进一步写出。这样可以节省端口和总线流量。节省流量对于避免部分写入未缓存的内存特别重要。

有六个写合并缓冲区(在奔腾4和Intel Xeon处理器上,CPUID签名的族编码为15,模型编码为3;有8个写合并缓冲区)。这些缓冲区中的两个可以写出更高的缓存级别,并释放出来以供其他写未命中。保证只有四个写合并缓冲区可同时使用。写合并适用于存储器类型WC;它不适用于内存类型UC。

Intel Core Duo和Intel Core Solo处理器的每个处理器内核中都有六个写合并缓冲区。基于英特尔酷睿微体系结构的处理器在每个内核中都有八个写合并缓冲区。从英特尔微体系结构代码名称Nehalem开始,有10个缓冲区可用于写合并。

写合并缓冲区用于所有存储器类型的存储。它们对于写入未缓存的内存特别重要...

我的问题是,在使用普通存储区(非临时存储区以外的其他存储区,即您所使用的存储区)时,写合并是否适用于WB内存区域(即您在用户程序中使用99.99%的时间的“普通”内存)正在使用99.99%的时间)。

上面的文字很难准确解释,因为自Core Duo时代以来没有更新过。您有说写梳理的部分“适用于WC存储器,但不适用于UC”,但是当然不包括所有其他类型,例如WB。后来,您发现“ [WC对于写入未缓存的内存特别重要”,这似乎与“不适用于UC部分”相矛盾。

那么,现代英特尔芯片上用于正常存储到WB存储器的写合并缓冲区是否有效?

Had*_*ais 5

是的,LFB 的写入组合和合并属性支持除 UC 类型之外的所有内存类型。您可以使用以下程序通过实验观察它们的影响。它需要两个参数作为输入:

  • STORE_COUNT:要顺序执行的 8 字节存储的数量。
  • INCREMENT:连续商店之间的步幅。

有 4 个不同的值INCREMENT特别有趣:

  • 64:所有存储都在唯一的缓存行上执行。写入合并和合并不会生效。
  • 0:所有存储都位于同一高速缓存行和该行内的同一位置。写合并在这种情况下生效。
  • 8:每8个连续的存储都在同一个缓存行中,但在该行内的不同位置。这种情况下写合并生效。
  • 4:连续存储的目标位置在同一缓存行内重叠。某些存储可能会跨越两个缓存行(取决于STORE_COUNT)。写入合并和合并都会生效。

还有另一个参数 ,ITERATIONS用于多次重复相同的实验以进行可靠的测量。您可以将其保持在 1000。

%define ITERATIONS 1000

BITS 64
DEFAULT REL

section .bss
align 64
bufsrc:     resb STORE_COUNT*64

section .text
global _start
_start:  
    mov ecx, ITERATIONS

.loop:
; Flush all the cache lines to make sure that it takes a substantial amount of time to fetch them.
    lea rsi, [bufsrc]
    mov edx, STORE_COUNT
.flush:
    clflush [rsi]
    sfence
    lfence
    add rsi, 64
    sub edx, 1
    jnz .flush

; This is the main loop where the stores are issued sequentially.
    lea rsi, [bufsrc]
    mov edx, STORE_COUNT
.inner:
    mov [rsi], rdx
    sfence ; Prevents potential combining in the store buffer.
    add rsi, INCREMENT
    sub edx, 1
    jnz .inner

; Spend sometime doing nothing so that all the LFBs become free for the next iteration.
    mov edx, 100000
.wait:
    lfence
    sub edx, 1
    jnz .wait

    sub ecx, 1
    jnz .loop

; Exit.    
    xor edi,edi
    mov eax,231
    syscall
Run Code Online (Sandbox Code Playgroud)

我推荐以下设置:

  • 使用 禁用所有硬件预取器sudo wrmsr -a 0x1A4 0xf。这确保它们不会干扰(或最小干扰)实验。
  • 将 CPU 频率设置为最大。这增加了在第一个缓存行到达 L1 之前完全执行主循环并导致 LFB 被释放的可能性。
  • 禁用超线程,因为 LFB 是共享的(至少从 Sandy Bridge 起,但不是在所有微体系结构上)。

L1D_PEND_MISS.FB_FULL性能计数器使我们能够捕捉写的关于合并的效果如何影响LFBs的可用性。它在 Intel Core 及更高版本上受支持。描述如下:

请求需要 FB(填充缓冲区)条目但没有可用条目的次数。请求包括可缓存/不可缓存的请求,即加载、存储或软件预取指令。

首先在没有内循环的情况下运行代码并确保它L1D_PEND_MISS.FB_FULL为零,这意味着刷新循环对事件计数没有影响。

下图绘制STORE_COUNT了 totalL1D_PEND_MISS.FB_FULL除以ITERATIONS

在此处输入图片说明

我们可以观察到以下几点:

  • 很明显,恰好有 10 个 LFB。
  • 当写入组合或合并是可能的时,L1D_PEND_MISS.FB_FULL对于任意数量的存储为零。
  • 当stride为64字节时,L1D_PEND_MISS.FB_FULL当存储数大于10时大于零。

后来你知道“[WC] 对于写入未缓存的内存特别重要”,似乎与“不适用于 UC 部分”相矛盾。

WC 和 UC 都被归类为不可缓存的。所以你可以把这两个语句放在一起推导出WC对于写入WC内存特别重要。

另请参阅:Write-Combining Buffer 位于何处?x86

  • 我误解了这个测试。我认为它正在做正确的事情。基本上它表明正在合并,否则我们希望较小的步幅测试显示相同的峰值。也就是说,在 L1 中未命中的存储不会位于存储缓冲区的头部,而是分配给它们一个填充缓冲区,因此存储缓冲区可以继续排空。它还表明,命中相同填充缓冲区的后续存储可以流入它们而不是阻塞。唯一可以添加的是检查`resource_stalls.sb` 以检查SB 是否按照我们的想法行事。 (2认同)