内存序之编译器指令重排序

环境:

  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恒成立。

posted on 2023-04-19 21:30  dchao  阅读(122)  评论(0编辑  收藏  举报

导航