首页 文章

锁定指令是否在弱有序访问之间提供了障碍?

提问于
浏览
5

在x86上, lock 等前缀指令(如 lock cmpxchg )除了原子操作外还提供屏障语义:对于回写内存区域的正常内存访问,读取和写入不按 lock -前缀指令重新排序,按照8.2.2节英特尔SDM第3卷:

读取或写入不能使用I / O指令,锁定指令或序列化指令进行重新排序 .

本节仅适用于回写内存类型 . 在同一个列表中,您会发现一个例外情况,它指出没有订购弱排序的商店:

读取不会与其他读取重新排序 . 写入不会与较旧的读取重新排序 . 写入内存不会与其他写入重新排序,但以下情况除外: - 使用非时间移动指令(MOVNTI,MOVNTQ,MOVNTDQ,MOVNTPS和MOVNTPD)执行的流存储(写入);和 - 字符串操作(参见第8.2.4.1节) .

注意,列表中的任何其他项目中的非时间指令没有例外,例如,在涉及锁定前缀指令的项目中 .

在本指南的各个其他部分中,提到当使用弱有序(非时间)指令时, mfence 和/或 sfence 指令可用于对存储器进行排序 . 这些部分通常不提及 lock -prefixed指令作为替代 .

所有这些让我不确定: lock -prefixed指令是否提供了 mfence 在WB内存上的弱有序(非时间)指令之间提供的完全屏障?同样的问题再次适用于WC内存的任何类型的访问 .

2 回答

  • 2

    锁定前缀的指令比Intel的 mfence 强大得多.AMD64的 mfence 是一个完全序列化的指令,所以它比Lock-prefixed指令强 . 还有32-bit x86 AMD processors支持 mfence ,我希望它的行为与AMD64相同 . 本答案的其余部分仅适用于英特尔的 mfence .

    紧接在锁定前缀指令之前或之后的 mfence 是多余的 . 从第8.2.5节:

    I / O指令,锁定指令,LOCK前缀和序列化指令强制处理器的排序更强 .

    (这里的锁定指令是指隐含 lock 的那些 . 在手册的其他地方,它们也明确地引用了前缀为_2360395的指令 . )

    这里“更强”意味着比第8.2.2节(问题中引用)中讨论的默认排序更强 . 也是从8.2.5:

    与I / O和锁定指令类似,处理器等待所有先前的指令完成,并且在执行序列化指令之前所有缓冲的写入都已耗尽到存储器 .

    第8.3节讨论了序列化指令,它根本没有提到锁定前缀 . 但它说:

    以下说明是内存排序说明,而不是序列化说明 . 这些耗尽了数据存储器子系统 . 它们不会序列化指令执行流:•非特权内存排序指令 - SFENCE,LFENCE和MFENCE .

    值得注意的是,锁定前缀不会像第8.3节中列出的那样进行序列化指令 . 主要区别在于锁定前缀允许获取以下指令 . 另外,关于软件预取指令不订购锁定前缀指令 . 从英特尔手册V2:

    PREFETCHh指令被认为是对这种推测行为的暗示 . 因为这种推测性提取可以在任何时间发生并且不依赖于指令执行,所以不对栅栏指令(MFENCE,SFENCE和LFENCE)或锁定存储器引用排序PREFETCHh指令 . 对于CLFLUSH和CLFLUSHOPT指令,其他PREFETCHh指令或任何其他通用指令,PREFETCHh指令也是无序的 . 它是针对序列化指令(如CPUID,WRMSR,OUT和MOV CR)进行排序的 .

    这同样适用于所有软件预取指令,而不仅仅是 PREFETCHh .

    使锁前缀比_2360397更强大的原因是它不仅提供了所有指令(软件预取除外)的序列化,还锁定了对共享内存的访问,以便所有其他逻辑处理器无法访问内存,直到它退休,从而提供原子性 .

    现在我've seen the quote from Necrolis'的答案说明了锁定前缀 may not serialize加载操作,它引用了弱有序的内存类型 . 但我认为这个陈述非常陈旧,并且在英特尔不想公开锁定前缀的完整保证的时候为非常老的处理器编写 . 此外,引用只说"may not",这并不是真的矛盾 .

    这也可以从AMD手册V2 7.4.2中确认:

    在发出I / O,锁定或序列化指令的存储器访问之前,所有先前的加载和存储都将完成到内存或I / O空间 . 在发出后续指令的加载或存储之前,与I / O和锁定指令相关联的所有加载和存储都完成到存储器(无缓冲存储) .


    脚注:(1)似乎Haswell,Ivy Bridge,Sandy Bridge和可能更早的微体系结构都有一个错误,其中锁定前缀的指令不能序列化NT负载,但 MFENCE 可以 . 在Skylake上,两个指令都无法序列化NT负载 . 见:Does lock xchg have the same behavior as mfence? . 然而,在Broadla和Skylake之后的处理器上,似乎两个指令都可以序列化NT负载 .


    @ PeterCordes的experiments表明,在Skylake上,锁定指令似乎不会阻止ALU指令无序执行,而 mfence 会对ALU指令进行序列化(可能与 lfence 存储缓冲区刷新一样,与锁定指令相同) . 但是,我认为这是一个实现细节 .

  • 5

    总线锁(通过 LOCK 操作码前缀)产生一个完整的栅栏*,但是,在WC存储器上它们不提供负载栅栏,这在英特尔64和IA-32架构软件开发人员手册,卷3A,8.1中有记载 . 2:

    对于P6系列处理器,锁定操作会序列化所有未完成的加载和存储操作(即等待它们完成) . 对于奔腾4和英特尔至强处理器,此规则也是如此,但有一个例外 . 引用弱有序内存类型(例如WC内存类型)的加载操作可能无法序列化 .

    *有关示例,请参阅英特尔64和IA-32架构软件开发人员手册,第3A卷,8.2.3.9

相关问题