我以为我了解如何处理L1D写入未命中,但是仔细思考就使我感到困惑。
这里是汇编语言片段:
;rdi contains some valid 64-bytes aligned pointer
;rsi contains some data
mov [rdi], rsi
mov [rdi + 0x40], rsi
mov [rdi + 0x20], rsi
假定[rdi]
和[rdi + 0x40]
行在l1d中未处于排他或修改状态。然后,我可以想象以下操作顺序:
mov [rdi], rsi
退休。mov [rdi], rsi
尝试将数据写入l1d。启动RFO,将数据放入WC缓冲区。mov [rdi + 0x40], rsi
退休([mov [rdi], rsi
已经退休,所以有可能)mov [rdi + 0x40], rsi
为连续的高速缓存行启动RFO,将数据放入WC缓冲区。mov [rdi + 0x20], rsi
退休([mov [rdi + 0x40], rsi
已退休,因此有可能)]mov [rdi + 0x20], rsi
注意到正在进行[rdi]
的RFO。数据被放入WC缓冲区。
BOOM![rdi]
RFO恰好在[rdi + 0x40]
RFO之前完成,因此现在可以将mov [rdi], rsi
和mov [rdi + 0x20], rsi
的数据提交到缓存中。它破坏了内存顺序。
如何处理这种情况以保持正确的内存顺序?
是的,将存储缓冲区提交到不跟踪顺序的WC缓冲区中将违反x86的强排序模型。
这是[[为什么我们可以确定x86 CPU实际上没有这样做,而是将存储数据保留在存储缓冲区1中。它必须按程序顺序从存储缓冲区提交到相干的L1d(即变得全局可见)。这意味着如果该行尚未处于“修改”或“互斥”状态,则等待RFO完成。存储缓冲区会为尚未到达存储缓冲区末尾的存储执行RFO,因此它可以通过运行多个RFO来实现内存级别的并行性,但是需要保留它们的顺序直到提交为止到L1d。
如@BeeOnRope在回答Where is the Write-Combining Buffer located? x86时所写
我的理解是,对于可缓存商店,只有RFO请求是 保留在LFB中,但是要存储的数据在存储缓冲区中等待] 直到将目标行提取到为其分配的LFB条目中。 以下的第2.4.5.2节 英特尔优化手册:
L1 DCache可以通过分配维护多达64个加载微操作 直到退休。它最多可以维护36个商店操作 分配,直到将存储值提交到缓存或写入 如果是非临时存储,则将它们添加到行填充缓冲区(LFB)。
退休后的RFO可以。问题将是将存储数据提交到无序的WC缓冲区中。
[我们知道some weakly-ordered RISCs microarchitectures definitely do merge stores before they commit,特别是创建高速缓存ECC颗粒的完整4字节或8字节写入,以避免RMW周期。但是,对于高速缓存行内狭窄或未对齐的存储区,Intel CPU不会受到任何惩罚。
一段时间以来,@ BeeOnRope和我以为有一些商店合并的证据,但是我们改变了主意。 Size of store buffers on Intel hardware? What exactly is a store buffer?有更多详细信息(以及指向较早讨论的链接)。
脚注1:英特尔将存储缓冲区+加载缓冲区统称为内存顺序缓冲区,因为它们需要彼此了解才能跟踪推测性早期加载。但是对于已退休的存储指令(更具体地说,是其“已分级”的存储缓冲区条目),它只是一个存储缓冲区,必须按程序顺序提交到L1d。