gcc 和 cpu_relax、smb_mb 等?
gcc and cpu_relax, smb_mb, etc.?
我一直在阅读编译器优化与 CPU 优化,以及 volatile
与内存障碍。
我不清楚的一件事是,我目前的理解是 CPU 优化和编译器优化是正交的。 IE。可以相互独立发生。
然而,文章 volatile considered harmful makes the point that volatile
should not be used. Linus's post 提出了类似的主张。 IIUC 的主要原因是,将变量标记为 volatile
会在访问该变量时禁用 all 编译器优化(即即使它们无害),同时仍然不提供保护反对内存重新排序。本质上,要点是它不是应该小心处理的 data,而是需要小心处理特定的 access pattern .
现在,volatile considered harmful 文章给出了以下等待标志的繁忙循环示例:
while (my_variable != what_i_want) {}
并指出编译器可以优化对 my_variable
的访问,以便它只发生一次而不是在循环中。文章声称,解决方案如下:
while (my_variable != what_i_want)
cpu_relax();
据说cpu_relax
充当编译器屏障(文章的早期版本说它是内存屏障).
我这里有几个缺口:
1) 暗示 gcc 对 cpu_relax
调用具有特殊知识,并且它转化为对编译器 和 的提示 CPU?
2) smb_mb()
之类的其他指令也是这样吗?
3) 如果 cpu_relax
本质上被定义为 C 宏,那么它是如何工作的?如果我手动扩展 cpu_relax
gcc 是否仍将其视为编译器障碍?我怎么知道 gcc 尊重哪些调用?
4) 就 gcc 而言,cpu_relax
的范围是什么?换句话说,当gcc看到cpu_relax
指令时,它不能优化的读取范围是什么?从 CPU 的角度来看,范围很广(内存屏障在读或写缓冲区中放置一个标记)。我猜想 gcc 使用较小的作用域——也许是 C 作用域?
是的,gcc 对 cpu_relax
或它扩展到的任何内容的语义有特殊的了解,并且必须将其翻译成硬件也会尊重语义的东西。
是的,任何类型的内存防护原语都需要编译器和硬件的特殊尊重。
看看宏扩展到什么,例如使用 "gcc -E" 编译并检查输出。您必须阅读编译器文档才能找出原语的语义。
内存栅栏的范围与编译器可能移动加载或存储的范围一样宽。从不在子例程调用中移动加载或存储的非优化编译器可能不需要过多关注表示为子例程调用的内存栅栏。跨翻译单元进行过程间优化的优化编译器需要在更大范围内跟踪内存栅栏。
您的问题中有一些与 cpu 和 smp 并发相关的微妙问题,需要您查看内核代码。这里有一些快速的想法,可以帮助您开始专门针对 x86 架构的研究。
这个想法是,您正在尝试执行并发操作,其中您的内核任务(参见内核源代码 sched.h 的结构 task_struct)与 my_variable 相比处于紧密循环中一个局部变量,直到它被另一个内核任务更改(或被硬件设备异步更改!)这是内核中的常见模式。
内核已移植到许多体系结构,每个体系结构都有一组特定的机器指令来处理并发。对于 x86,cpu_relax 映射到 PAUSE 机器指令。它允许 x86 CPU 更有效地 运行 自旋锁,以便锁变量更新更容易通过自旋 CPU 可见。 GCC 将像执行任何其他函数一样执行 function/macro。如果 cpu_relax 从循环中移除,那么 gcc 可以认为循环无效并将其移除。请查看 Intel X86 软件手册中的 PAUSE 指令。
smp_mb 是一条 x86 内存栅栏指令,用于刷新内存缓存。一个 CPU 可以在其缓存中更改 my_variable,但其他 CPU 将看不到它。 smp_mb 提供按需缓存一致性。查看英特尔 X86 软件手册以获取 MFENCE/LFENCE 说明。
请注意,smp_mb() 会刷新 CPU 缓存,因此它可能是一项昂贵的操作。当前的英特尔 CPU 拥有巨大的缓存(~6MB)。
如果在 x86 上展开 cpu_relax,它将显示 asm volatile("rep; nop" :::: "memory").这不是编译器障碍,而是 GCC 不会优化的代码。请参阅屏障宏,它是 asm volatile("": : : "memory") 以获得 GCC 提示。
我不明白你说的 "scope of cpu_relax" 是什么意思。一些可能的想法:它是 PAUSE 机器指令,类似于 ADD 或 MOV。 PAUSE 只会影响当前 CPU。 PAUSE 允许 CPUs 之间更有效的缓存一致性。
我只是稍微看了看 PAUSE 指令 - 一个额外的 属性 是它防止 CPU 在离开紧 loop/spinlock 时进行乱序内存推测.我不清楚 THAT 是什么意思,但我想它可以简单地指示变量中的错误值?还有很多问题....
我一直在阅读编译器优化与 CPU 优化,以及 volatile
与内存障碍。
我不清楚的一件事是,我目前的理解是 CPU 优化和编译器优化是正交的。 IE。可以相互独立发生。
然而,文章 volatile considered harmful makes the point that volatile
should not be used. Linus's post 提出了类似的主张。 IIUC 的主要原因是,将变量标记为 volatile
会在访问该变量时禁用 all 编译器优化(即即使它们无害),同时仍然不提供保护反对内存重新排序。本质上,要点是它不是应该小心处理的 data,而是需要小心处理特定的 access pattern .
现在,volatile considered harmful 文章给出了以下等待标志的繁忙循环示例:
while (my_variable != what_i_want) {}
并指出编译器可以优化对 my_variable
的访问,以便它只发生一次而不是在循环中。文章声称,解决方案如下:
while (my_variable != what_i_want)
cpu_relax();
据说cpu_relax
充当编译器屏障(文章的早期版本说它是内存屏障).
我这里有几个缺口:
1) 暗示 gcc 对 cpu_relax
调用具有特殊知识,并且它转化为对编译器 和 的提示 CPU?
2) smb_mb()
之类的其他指令也是这样吗?
3) 如果 cpu_relax
本质上被定义为 C 宏,那么它是如何工作的?如果我手动扩展 cpu_relax
gcc 是否仍将其视为编译器障碍?我怎么知道 gcc 尊重哪些调用?
4) 就 gcc 而言,cpu_relax
的范围是什么?换句话说,当gcc看到cpu_relax
指令时,它不能优化的读取范围是什么?从 CPU 的角度来看,范围很广(内存屏障在读或写缓冲区中放置一个标记)。我猜想 gcc 使用较小的作用域——也许是 C 作用域?
是的,gcc 对
cpu_relax
或它扩展到的任何内容的语义有特殊的了解,并且必须将其翻译成硬件也会尊重语义的东西。是的,任何类型的内存防护原语都需要编译器和硬件的特殊尊重。
看看宏扩展到什么,例如使用 "gcc -E" 编译并检查输出。您必须阅读编译器文档才能找出原语的语义。
内存栅栏的范围与编译器可能移动加载或存储的范围一样宽。从不在子例程调用中移动加载或存储的非优化编译器可能不需要过多关注表示为子例程调用的内存栅栏。跨翻译单元进行过程间优化的优化编译器需要在更大范围内跟踪内存栅栏。
您的问题中有一些与 cpu 和 smp 并发相关的微妙问题,需要您查看内核代码。这里有一些快速的想法,可以帮助您开始专门针对 x86 架构的研究。
这个想法是,您正在尝试执行并发操作,其中您的内核任务(参见内核源代码 sched.h 的结构 task_struct)与 my_variable 相比处于紧密循环中一个局部变量,直到它被另一个内核任务更改(或被硬件设备异步更改!)这是内核中的常见模式。
内核已移植到许多体系结构,每个体系结构都有一组特定的机器指令来处理并发。对于 x86,cpu_relax 映射到 PAUSE 机器指令。它允许 x86 CPU 更有效地 运行 自旋锁,以便锁变量更新更容易通过自旋 CPU 可见。 GCC 将像执行任何其他函数一样执行 function/macro。如果 cpu_relax 从循环中移除,那么 gcc 可以认为循环无效并将其移除。请查看 Intel X86 软件手册中的 PAUSE 指令。
smp_mb 是一条 x86 内存栅栏指令,用于刷新内存缓存。一个 CPU 可以在其缓存中更改 my_variable,但其他 CPU 将看不到它。 smp_mb 提供按需缓存一致性。查看英特尔 X86 软件手册以获取 MFENCE/LFENCE 说明。
请注意,smp_mb() 会刷新 CPU 缓存,因此它可能是一项昂贵的操作。当前的英特尔 CPU 拥有巨大的缓存(~6MB)。
如果在 x86 上展开 cpu_relax,它将显示 asm volatile("rep; nop" :::: "memory").这不是编译器障碍,而是 GCC 不会优化的代码。请参阅屏障宏,它是 asm volatile("": : : "memory") 以获得 GCC 提示。
我不明白你说的 "scope of cpu_relax" 是什么意思。一些可能的想法:它是 PAUSE 机器指令,类似于 ADD 或 MOV。 PAUSE 只会影响当前 CPU。 PAUSE 允许 CPUs 之间更有效的缓存一致性。
我只是稍微看了看 PAUSE 指令 - 一个额外的 属性 是它防止 CPU 在离开紧 loop/spinlock 时进行乱序内存推测.我不清楚 THAT 是什么意思,但我想它可以简单地指示变量中的错误值?还有很多问题....