内存保护键内存重新排序
Memory Protection Keys Memory Reordering
阅读英特尔关于内存保护密钥 (MPK) 的 SDM 并不建议 wrpkru
指令作为序列化或隐式强制执行内存排序。
首先,如果它没有强制执行某种顺序,这令人惊讶,因为有人会怀疑程序员不希望围绕 wrpkru
的内存访问乱序执行。
其次,那是否意味着wrpkru
需要被lfence
包围?
我假设 CPU 一如既往地保留了 运行 程序顺序中单个线程的错觉。这是乱序执行的基本规则。 wrpkru
之前的访问使用旧 PKRU 完成,之后的访问使用新 PKRU 完成。
就像修改 MXCSR 影响后面的 FP 指令而不影响前面的指令,或者修改段寄存器影响后面但不影响前面的指令一样 loads/stores。
是否要重命名 PKRU、MXCSR 或段寄存器取决于实现。如果它不重命名 PKRU,那么它必须在更改 PKRU 并允许稍后的 loads/stores 执行之前完成所有未决的 loads/stores。 (即 wrpkru
的微代码可以包含 lfence
的微指令,如果它是这样实现的话。)
所有内存访问都依赖于最后的wrpkru
指令,最后写入相关段寄存器,最后写入cr3
(顶级页table),以及最后一次更改权限级别(syscall
/ iret
/ 其他)。同样在该位置的最后一家商店,您永远不需要围栏来查看您自己最近的商店。 CPU 架构师需要构建运行速度快的硬件,同时保持程序顺序的错觉。
例如Intel CPUs 因为至少 Core2 已经重命名了 x87 FP 控制字,所以通过将 x87 舍入模式更改为截断然后返回到最近的方式来实现 (int)fp_var
的旧二进制文件不会序列化 FPU。根据 Agner Fog 的测试,一些 CPU 会重命名段寄存器,但我的测试表明 Skylake 不会:.
我不熟悉 MPK,但是只要它们都使用正确的 PKRU 值并且不违反任何 x86 的正常内存,为什么内存访问乱序会成为问题-排序规则?
(仅允许其他线程看到 StoreLoad 重新排序。在内部 CPU 可以比 "supposed to" 更早地执行加载,但请验证缓存行在此之前未失效它在架构上允许加载的点。
这就是内存顺序缓冲区的作用。)
在C/C++中,当然你需要某种屏障来防止编译时围绕包装函数的访问重新排序。通常非内联函数调用就足够了,比如 pthread_mutex_lock()
。 .
此答案的前半部分是关于组装中的订购。
阅读英特尔关于内存保护密钥 (MPK) 的 SDM 并不建议 wrpkru
指令作为序列化或隐式强制执行内存排序。
首先,如果它没有强制执行某种顺序,这令人惊讶,因为有人会怀疑程序员不希望围绕 wrpkru
的内存访问乱序执行。
其次,那是否意味着wrpkru
需要被lfence
包围?
我假设 CPU 一如既往地保留了 运行 程序顺序中单个线程的错觉。这是乱序执行的基本规则。 wrpkru
之前的访问使用旧 PKRU 完成,之后的访问使用新 PKRU 完成。
就像修改 MXCSR 影响后面的 FP 指令而不影响前面的指令,或者修改段寄存器影响后面但不影响前面的指令一样 loads/stores。
是否要重命名 PKRU、MXCSR 或段寄存器取决于实现。如果它不重命名 PKRU,那么它必须在更改 PKRU 并允许稍后的 loads/stores 执行之前完成所有未决的 loads/stores。 (即 wrpkru
的微代码可以包含 lfence
的微指令,如果它是这样实现的话。)
所有内存访问都依赖于最后的wrpkru
指令,最后写入相关段寄存器,最后写入cr3
(顶级页table),以及最后一次更改权限级别(syscall
/ iret
/ 其他)。同样在该位置的最后一家商店,您永远不需要围栏来查看您自己最近的商店。 CPU 架构师需要构建运行速度快的硬件,同时保持程序顺序的错觉。
例如Intel CPUs 因为至少 Core2 已经重命名了 x87 FP 控制字,所以通过将 x87 舍入模式更改为截断然后返回到最近的方式来实现 (int)fp_var
的旧二进制文件不会序列化 FPU。根据 Agner Fog 的测试,一些 CPU 会重命名段寄存器,但我的测试表明 Skylake 不会:
我不熟悉 MPK,但是只要它们都使用正确的 PKRU 值并且不违反任何 x86 的正常内存,为什么内存访问乱序会成为问题-排序规则?
(仅允许其他线程看到 StoreLoad 重新排序。在内部 CPU 可以比 "supposed to" 更早地执行加载,但请验证缓存行在此之前未失效它在架构上允许加载的点。 这就是内存顺序缓冲区的作用。)
在C/C++中,当然你需要某种屏障来防止编译时围绕包装函数的访问重新排序。通常非内联函数调用就足够了,比如 pthread_mutex_lock()
。
此答案的前半部分是关于组装中的订购。