Memory GCC&x27;s读/写指令的重新排序

Memory GCC&x27;s读/写指令的重新排序,memory,gcc,linux-kernel,cpu,compiler-optimization,Memory,Gcc,Linux Kernel,Cpu,Compiler Optimization,Linux的同步原语(自旋锁、互斥锁、RCU)使用内存屏障指令强制内存访问指令重新排序。这种重新排序可以由CPU本身或编译器来完成 有人能给出一些GCC生成的代码的例子吗?我主要对x86感兴趣。我问这个问题的原因是为了理解GCC如何决定哪些指令可以重新排序。不同的x86 mirco体系结构(例如:sandy bridge和ivy bridge)使用不同的缓存体系结构。因此,我想知道GCC如何进行有效的重新排序,以帮助提高执行性能,而不考虑缓存体系结构。一些示例C代码和重新排序的GCC生成的代码将

Linux的同步原语(自旋锁、互斥锁、RCU)使用内存屏障指令强制内存访问指令重新排序。这种重新排序可以由CPU本身或编译器来完成


有人能给出一些GCC生成的代码的例子吗?我主要对x86感兴趣。我问这个问题的原因是为了理解GCC如何决定哪些指令可以重新排序。不同的x86 mirco体系结构(例如:sandy bridge和ivy bridge)使用不同的缓存体系结构。因此,我想知道GCC如何进行有效的重新排序,以帮助提高执行性能,而不考虑缓存体系结构。一些示例C代码和重新排序的GCC生成的代码将非常有用。谢谢

GCC可能进行的重新排序与(x86)CPU可能进行的重新排序无关

让我们从编译器重新排序开始。C语言规则是这样的,当它们之间出现一个序列点时,GCC被禁止重新排序
volatile
加载和存储内存访问,或者删除它们(感谢这一澄清)。也就是说,在程序集输出中,这些内存访问将出现,并将按照您指定的顺序进行精确排序。另一方面,非易失性访问可以相对于所有其他访问重新排序,
易失性
与否,前提是(根据假设规则)计算的最终结果相同

例如,C代码中的非易失性加载可以按照代码所说的次数进行,但顺序不同(例如,如果编译器认为在更多寄存器可用时更早或更晚进行加载更方便)。它可以做的比代码所说的少(例如,如果在大型表达式的中间的寄存器中登记的值仍然可用)。或者甚至可以删除它(例如,如果编译器可以证明加载无效,或者如果它将变量完全移动到寄存器中)

若要防止编译器在其他时间重新排序,必须使用编译器特定的屏障。GCC使用
\uuuuuasm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu用于此目的

这与CPU重新排序不同,也称内存排序模型。古代CPU精确地按照指令在程序中出现的顺序执行指令;这称为程序排序,或强内存排序模型。然而,现代CPU有时会借助“欺骗”来运行得更快,方法是稍微削弱内存模型

x86 CPU削弱内存模型的方式在《英特尔软件开发人员手册》第3卷第8章第8.2.2节“P6及更新处理器系列中的内存排序”中有详细说明。部分内容如下:

  • 读取不会与其他读取一起重新排序
  • 写入不会与旧的读取一起重新排序
  • 除了[某些]例外,对内存的写入不会与其他写入一起重新排序
  • 读操作可以通过较旧的写入操作重新排序到不同的位置,但不能通过较旧的写入操作重新排序到同一位置
  • 不能使用I/O指令、锁定指令或序列化指令对读取或写入进行重新排序
  • 读取无法传递早期LFENCE和MFENCE指令
  • 写入无法传递早期的LFENCE、SFENCE和MFENCE指令
  • LFENCE指令无法通过早期读取
  • SFENCE指令无法传递早期写入
  • MFENCE指令无法通过早期读取或写入
在第8.2.3节“说明内存排序原则的示例”中,它还提供了可以和不能重新排序的非常好的示例

如您所见,可以使用围栏指令来防止x86CPU对内存访问进行不适当的重新排序

最后,您可能会对link感兴趣,它将进一步详细介绍,并附带您渴望的组装示例。

  • C语言规则禁止GCC对易失性负载进行重新排序,禁止存储内存访问,也禁止删除它们
这是不正确的,而且具有误导性。C规范不提供此类保证。 看

该标准鼓励编译器避免对易失性对象的访问进行优化,但将实现定义为什么构成易失性访问。最低要求是,在一个序列点上,所有之前对易失性对象的访问都已稳定,并且没有发生后续访问。因此,实现可以自由地对序列点之间发生的易失性访问进行重新排序和组合,但不能对跨序列点的访问进行重新排序和组合


传统上,程序员依赖volatile作为一种廉价的同步方法,但这不再是一种可靠的方法。

顺便说一句,顺序执行不足以保证程序顺序;缓存一致性信息的传输需要时间,并且在缓存未命中时可以缓冲写操作。如果是正确的,C直到C11(即最近)才有内存模型标准。另外,nice linked reference.@PaulA.Clayton-AFAIK缓存一致性是同步的、精确的、100%可靠的;它从不会太晚得到任何信息。缓存不会错过火车,它们会封锁火车直到上车。@curiousguy一致性(跨越不同地址)不同于一致性(相同地址)。即使是像total store order这样适度宽松的内存一致性模型也允许顺序一致性不允许的可见性顺序。@PaulA.Clayton是的,但如果有,1)“缓存一致性信息的通信时间”2“缓存未命中时可以缓冲写操作”会起到什么作用?如果进行缓冲写操作,完全删除缓存(抽象地)会恢复内存一致性吗