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

时间:2018-11-22 17:09:40

标签: performance x86 intel cpu-architecture

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

例如,在Intel 64 and IA-32 Architectures Optimization Reference Manual部分的“ 3.6.10写合并”中,以以下描述开始(添加了重点):

  

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

     

•写   错过了一级缓存,它允许多个存储到同一个   在读取缓存行以获取所有权(RFO)之前发生的缓存行   从缓存/内存层次结构的更远处开始。然后其余的线   读取,未写入的字节与   返回行中未修改的字节。

     

•写合并允许   多次写入将被组合并进一步写入高速缓存中   层次结构为单位。这样可以节省端口和总线流量。节省交通   对于避免部分写入未缓存尤为重要   记忆。

     

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

     

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

     

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

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

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

现代英特尔芯片上用于正常存储到WB存储器的写合并缓冲区是否也存在?

1 个答案:

答案 0 :(得分:3)

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

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

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

  • 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

我建议以下设置:

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

L1D_PEND_MISS.FB_FULL性能计数器使我们能够捕获有关写入组合对LFB可用性的影响的效果。在Intel Core和更高版本上受支持。描述如下:

  

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

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

下图将STORE_COUNT相对于L1D_PEND_MISS.FB_FULL除以ITERATIONS的总和。

enter image description here

我们可以观察到以下情况:

  • 很明显,总共有10个LFB。
  • 当可能进行写合并或合并时,L1D_PEND_MISS.FB_FULL对于任何数量的存储都为零。
  • 跨度为64字节时,如果存储数量大于10,则L1D_PEND_MISS.FB_FULL大于零。
  

您后来发现,“ [WC对于写给   未缓存的内存”,似乎与“不适用于UC部分”相矛盾。

WC和UC均被归类为不可固定。因此,您可以将这两个语句放在一起得出WC对于写入WC内存特别重要。

另请参阅:Where is the Write-Combining Buffer located? x86