环境:
Windows平台:win7_64旗舰版、VS2019
Linux平台:CentOS Linux relase 7.2.1511、GCC_4.8.5-4
场景:
为了提高性能,编译器会对指令进行重新排序,在多线程环境下指令的乱序执行会造成无法预测的行为。
开始:
一、指令重排序实例
int a = 0, b =0; void test() { a = b + 1; b = 2; } int main() { test(); return 0; }
分析上面的代码逻辑,当b=2时,a=1应该恒成立。
GCC编译后生成的汇编代码为
0x0000000000400f10 mov 0x2011ba(%rip),%eax # 0x6020d0 <b> //将b的值存储到eax寄存器 0x0000000000400f16 xor %esi,%esi 0x0000000000400f18 mov $0x2,%edi 0x0000000000400f1d movl $0x2,0x2011a9(%rip) # 0x6020d0 <b> //将2存储到b 0x0000000000400f27 add $0x1,%eax //将eax寄存器的值加1 0x0000000000400f2a mov %eax,0x2011b0(%rip) # 0x6020e0 <a> //将eax寄存器的值存储到a //简单来说,就是先将b的值保存起来(保存到eax寄存器),然后将2存储到b,再把eax的值加1存储到a,可见编译器对写入顺序进行重新排序。
VS2019编译后生成的汇编代码为
00D010F4 mov eax,dword ptr ds:[00D0302Ch] //将b的值存储到eax寄存器 00D010F9 inc eax //将eax寄存器的值加1 00D010FA mov dword ptr ds:[0D0302Ch],2 //将2存储到b 00D01104 mov dword ptr ds:[00D03028h],eax //将eax寄存器的值存储到a //简单来说,就是先将b的值保存到eax寄存器,然后将2存储到b,再把eax的值加1存储到a,可见编译器对写入顺序进行重新排序。
不考虑CPU内存顺序的情况下,在GCC和VS2019中,从其他线程看当b=2时,a的值可能为0,即b=2时,a=1不成立。
二使用std::atomic_signal_fence禁止写指令重排序
int a = 0, b =0; void test() { a = b + 1; std::atomic_signal_fence(std::memory_order_release); // 添加编译器写屏障 b = 2; } int main() { test(); return 0; }
GCC 编译后生成的汇编代码为
0x0000000000400f10 mov 0x2011ba(%rip),%eax # 0x6020d0 <b> //将b的值存储到eax寄存器 0x0000000000400f16 add $0x1,%eax //将eax寄存器的值加1 0x0000000000400f19 mov %eax,0x2011c1(%rip) # 0x6020e0 <a> //将eax寄存器的值存储到a 0x0000000000400f1f xor %esi,%esi 0x0000000000400f21 mov $0x2,%edi 0x0000000000400f26 movl $0x2,0x2011a0(%rip) # 0x6020d0 <b> //将2存储到b
VS2019编译后生成的汇编代码为
00B810F4 mov eax,dword ptr ds:[00B8302Ch] //将b的值存储到eax寄存器 00B810F9 inc eax //将eax寄存器的值加1 00B810FA mov dword ptr ds:[00B83028h],eax //将eax寄存器的值存储到a 00B810FF lea eax,[esp+0Ch] 00B81103 mov dword ptr ds:[0B8302Ch],2 //将2存储到b
不考虑CPU内存顺序的情况下,在GCC和VS2019中,从其他线程看当b=2时,a的值一定为1,即b=2时,a=1恒成立。
标签:存储,mov,eax,编译器,屏障,寄存器,排序,ptr From: https://www.cnblogs.com/dongc/p/17334706.html