首页 > 其他分享 >编译器指令重排序问题(使用编译器屏障)

编译器指令重排序问题(使用编译器屏障)

时间:2023-04-19 21:33:27浏览次数:37  
标签:存储 mov eax 编译器 屏障 寄存器 排序 ptr

环境:

  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

相关文章

  • 冒泡排序
    问题描述:键入N个整数进行升序排序。 1.第一行输入N表示输入的数字的个数  2.用循环输入数字;  3.定义i控制比较次数,定义j控制每次比较的次数;  4.如果后面的数小于前面的数,将两数位置交换。 #include<iostream>usingnamespacestd;intmain(){ intN,a[1000]; ......
  • el-table拖动排序
    html<el-tableref="multipleTable":data="tableData"align="left"borderclass="mytable"row-key="id"><el-table-column:index="indexMethod"align="center"type=&q......
  • 1.8冒泡排序
    1.问题描述把N个整数按照升序排列2.问题分析。利用数组储存,然后相邻数组进行排序。3.代码#include<iostream>#include<string>usingnamespacestd;intmain(){ intn[10]; inti,j; inttemp;  cout<<"请输入十个数字!"<<endl; for(i=0;i<10;i++){ cin>&......
  • 推排序 Verilog实现原理
    引言推排序常常应用在操作系统的任务调度中,尝试使用硬件对堆排序进行实现,在实现的过程中不使用function和tasks语法,即真·硬件实现参考的博客也就这一个博客有介绍堆排序的Verilog实现原理堆排序还需要复习一遍吗?我肯定是要的菜鸟-堆排序图解排序算法(三)之堆排序可以......
  • 第六周--冒泡排序
    题目描述读入N个整数,利用冒泡排序法对这些数排序,输出排序后的N个数,两个数之间用空格间隔。这里排序指的是升序。输入格式两行,第一行一个正整数N,表示待排序的数的个数。第二行为N个整数。输出格式一行,排序后的N个数。输入输出样例输入 542451输出 124......
  • 一千个需求如何快速排序?MoSCoW排序法用上了!【No.2】
    什么是MoSCoW排序法?莫斯科排序法是一种优先级排序法,用于管理需求、任务或功能列表。该方法可以帮助团队确定哪些需求、任务或功能是最重要的,并决定在特定时间段内是否需要完成它们。所以在对需求进行排序时,可以从以下维度考虑:能为业务目标产出高价值的需求优先做;节省时间、人......
  • 一些排序相关典题
    HDU6231&P2824HDU6231K-thNumber给你一个长度为\(n\)的序列\(A\),有一个初始为空的序列\(B\),把\(A\)中所有子区间的第\(K\)大加入序列\(B\)中,求\(B\)中的第\(M\)大\(n\le10^5,K\len\)考虑二分答案,假设当前答案是\(x\),把原序列中所有\(<x\)的元素变成\(......
  • List<Integer>排序
    List<Integer>list=newArrayList<Integer>();从小到大方法:Collections.sort(list);从大到小方法:Collections.sort(list,Collections.reverseOrder());  Java8将List<Integer>转换成以逗号分割的String字符串publicstaticvoidmain(String[]args){List<Int......
  • 冒泡排序
    冒泡排序的个人理解:<!--冒泡排序--><script>vararr=[9,8,7,6,5,4,3,2,1]//定义一个数组for(letj=0;j<arr.length-1/*倒数第二论比较剩下最小值后,不必再进行下一次比较*/;j++){for(leti=0;i<arr.length......
  • 选择排序
    选择排序的个人理解:先假定数组中的第0个就是最小的数字的索引然后遍历数组,只要有一个数字比我小,那么就替换之前记录的索引直到数组遍历结束后,就能找到最小的那个索引,然后让最小的索引换到第0个的位置再来第二趟遍历,假定第1个是最小的数字的索引在遍历一次数......