x86处理器gcc中编译器屏障的运行时开销

x86处理器gcc中编译器屏障的运行时开销,gcc,memory,assembly,linux-kernel,inline-assembly,Gcc,Memory,Assembly,Linux Kernel,Inline Assembly,我正在研究在x86env中使用编译器屏障(在gcc中)的副作用/运行时开销 编译器屏障:asm易失性(:“内存”) GCC文档讲述了一些有趣的事情() 摘录: “memory”clobber告诉编译器汇编代码 对中列出的项目以外的项目执行内存读取或写入 输入和输出操作数(例如,访问内存 由其中一个输入参数指向)以确保内存包含 正确的值,GCC可能需要刷新特定的寄存器值 执行asm之前的内存。此外,编译器不会假定 asm之前从内存中读取的任何值在asm之后保持不变 这个asm;它会根据需要重新加载

我正在研究在x86env中使用编译器屏障(在gcc中)的副作用/运行时开销

编译器屏障:asm易失性(:“内存”)

GCC文档讲述了一些有趣的事情()

摘录:

“memory”clobber告诉编译器汇编代码 对中列出的项目以外的项目执行内存读取或写入 输入和输出操作数(例如,访问内存 由其中一个输入参数指向)以确保内存包含 正确的值,GCC可能需要刷新特定的寄存器值 执行asm之前的内存。此外,编译器不会假定 asm之前从内存中读取的任何值在asm之后保持不变 这个asm;它会根据需要重新加载它们。使用“内存”重击器 有效地为编译器形成读/写内存屏障

问题:

1) 刷新哪些寄存器值

2) 为什么需要冲洗

3) 例子


4) 除了寄存器刷新,还有其他开销吗?

其他线程可能有指针指向的每个内存位置都需要在屏障之前更新,并在屏障之后重新加载。因此,任何存在于寄存器中的值都需要存储(如果是脏的),或者如果寄存器中的值只是仍在内存中的值的副本,则需要“忘记”

请参阅gcc开发人员的这段引文:
“内存”
clobber只包括可以间接访问的内存(因此可能是在这个或另一个编译单元中获取的地址)

除了寄存器刷新,还有其他开销吗

屏障可以阻止优化,比如从循环中删除存储,但这通常就是使用屏障的原因。确保循环计数器和循环变量是未将其地址传递给编译器看不到的函数的局部变量,否则它们必须在循环中溢出/重新加载。让引用逃逸函数始终是优化的一个潜在问题,但这几乎可以保证代码会有更糟糕的障碍


为什么?

这是一个障碍的全部要点:因此值被同步到内存,防止编译时重新排序

asm volatile(:“memory”)
与(而不是
原子线程围栏
,它需要
mfence
指令才能在x86上实现)


示例:


请参阅Jeff Preshing的文章,了解更多有关原因以及实际x86 asm的示例。

实际上,如果您阅读同一页,您将看到与之相关的示例。哪些寄存器需要刷新到内存取决于asm对寄存器的实际操作。只是强调一下:为了获得最大性能,编译器会尝试生成代码,尽可能将值保留在寄存器中(理想情况下,直到它们超出范围),只有在寄存器用完时才刷新到内存,某些东西需要访问实际内存(比如:fwrite),或者当明确要求刷新它们时(内存阻塞器、函数调用等)。请注意,此“调用”不会生成实际的汇编指令。它只是告诉编译器,此时,代码必须以某种方式组织,即使编译器认为这种方式可能不是最有效的。