volatile关键字及编译器指令乱序总结

本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论。     

以下是我搭建的博客地址: http://itblogs.ga/blog/20150329150706/    欢迎到这里阅读文章。

volatile关键字

volatile关键字用来修饰一个变量,提示编译器这个变量的值随时会改变。通常会在多线程、信号处理、中断处理、读取硬件寄存器等场合使用。

程序在执行时,通常将数据(变量的值)从内存的读到寄存器中,然后进行运算,此后对该变量的处理,都是直接访问寄存器就可以了,不再访问内存,因为 访存的代价是很高的(这块是访问寄存器还是重新访存加载到寄存器是编译器在编译阶段就决定了的)。但在上述说的几种情况下,内存会被另一个线程或者信号处 理函数、中断处理函数、硬件改掉,这样,代码只访问寄存器的话,永远得不到真实的值。

   

对这样的变量(会在多线程、线程与信号、线程与中断处理中共同访问的,或者硬件寄存器),在定义时都会加上volatile关键字修饰。这样编译器 在编译时,编译出的指令会重新访存,这样就能保证拿到正确的数据了。但这里需要注意的是,编译器只能做到让指令重新访问内存,而不是直接使用寄存器中的 值,这些和缓存没有关系,具体执行时指令是访问内存还是访问的缓存,编译器也无法干预。

   

另外,除了使用寄存器来避免多次访存外,编译器有时可能直接将变量全部优化掉,使用常数代替。比如:

int main()
{
    int a = 1;
    int b = 2;

    printf("a = %d, b = %d \n", a, b);
}

  

编译器可能直接优化为:     

int main()
{
    printf("a = %d, b = %d \n", 1, 2);
}

  

  如果对ab的声明加了 volatile关键字,编译器将不在做这样的优化。

             

还有,对所有volatile变量,编译器在编译阶段保证不会将访问volatile变量的指令进行乱序重排。

    

   

  指令乱序

那么什么是指令乱序,指令乱序是为了提高性能,而导致的执行时的指令顺序和代码写的顺序不一致。指令乱序有编译期间指令乱序和执行时指令乱序。

执行时指令乱序是CPU的一个特性,这块比较复杂,不再这里提及。我们只需要知道在x86/x64的体系架构下,程序员一般不需要关注执行时指令乱序(不需要关注不代表没有)。

编译期间指令乱序是指在编译成二进制代码时,编译器为了所谓的优化进行了指令重排,导致二进制指令的顺序和我们写的代码的顺序是不一致的。

比如以下代码:

int a;
int b;

int main()
{
    a = b + 1;
    b = 0;
}

会被优化成(实际上在汇编阶段进行的乱序优化,优化后的代码也只能以汇编的方式查看,这里只是拿C代码举例说明一下):

int a;
int b;

int main()
{
    b = 0;
    a = b + 1;
}

对加上volatile关键字的变量的访问,编译器不会进行指令乱序的优化,保证volatile变量的访问顺序和代码写的是一样的。比如如下代码不会优化:

volatile int a;
volatile int b;

int main()
{
    a = b + 1;
    b = 0;
}

  

但是以下代码,依然会乱序,因为编译器只是保证volatile变量访问的顺序,对于非volatile变量之间,以及volatile以及非volatile变量之间的顺序,编译器还是会优化。

int a;
volatile int b;

int main()
{
    a = b + 1;
    b = 0;
}

  

       

asm volatile ("" : : : "memory");

一般编程时如果使用到volatile关键字,那么基本上都需要考虑编译器指令乱序的问题。解决编译器指令乱序所带来的问题,除了上面将必要的变量声明为volatile,还可以使用下面一条嵌入式汇编语句:

1asm volatile ("" : : : "memory");

这是一条空汇编语句,只是告诉编译器,内存发生了变化。编译器遇到这条语句后,会生成访存更新寄存器的指令,将所有的寄存器的值更新一遍。这里是编译器遇到这条语句额外生成了一些代码,而不是CPU遇到这条语句执行了一些处理,因为这条语句本身并没有CPU指令与之对应。

由于编译器知道这条语句之后内存发生了变化,编译器在编译时就会保证这条语句上下的指令不会乱,即这条语句上面的指令,不会乱序到语句下面,语句下面的指令不会乱序到语句上面。

利用编译器这个功能,程序员可以:

1、利用这条语句,强制程序访存,而不是使用寄存器中的值,作为使用volatile关键字的一个替代手段;

2、在不允许乱序的两个语句之间插入这条语句从而保证不会被编译器乱序。

   

下面看一个应用的例子,两个线程访问共享的全局变量:

#define ARRAY_LEN 12

volatile int flag = 0;
int a[ARRAY_LEN];

pthread1()
{
    a[ARRAY_LEN - 1] = 10; <br>    asm volatile ("" : : : "memory");
    flag = 1;
}

pthread2()
{
    int sum = 0;

    if(flag == 0) {
        sum += a[ARRAY_LEN - 1];
    }    
}线程2假定flag==1时,线程1已经将数据放到数组中了。但实际上,如果没有  asm volatile ("" : : : "memory"),线程1并不能保证flag = 1在数组赋值之后。原因就是我们前面提到的编译器指令乱序。

     

指令乱序是一个比较复杂的话题,我们这里只考虑了编译器指令乱序,在intel架构的CPU上,基本上考虑到这些就足够了。但在弱指令序的CPU上,比如mips,了解这些还远远不够。本文不打算展开CPU指令乱序的话题,感兴趣的可以参考以下文章了解以下:

Memory Reordering Caught in the Act
This Is Why They Call It a Weakly-Ordered CPU

volatile关键字的使用

volatile关键字使用和const一致,下面是一个总结:

char const * pContent;       // *pContent是const,   pContent可变
(char *) const pContent;     //  pContent是const,  *pContent可变
char* const pContent;        //  pContent是const,  *pContent可变
char const* const pContent;  //  pContent 和       *pContent都是const

  

沿着*号划一条线,如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

   

   

参考资料

Memory Ordering at Compile Time


以下是我搭建的博客地址:
原文链接:http://itblogs.ga/blog/20150329150706/ 转载请注明出处

    

转载于:https://www.cnblogs.com/jintianfree/p/4375934.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/492407.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

求5阶矩阵其对角线上所有元素之和

1、代码如下&#xff1a; // test.cpp : Defines the entry point for the console application. // /* 输入一个5*5的矩阵&#xff0c;然后输出其对角线上所有元素之和。 当求N阶矩阵其对角线上所有元素之和时&#xff0c;只要把以下程序中所有的5改成N,4改成N-1即可。*/ #inc…

DARPA“终身学习机器”项目取得重大进展

来源&#xff1a;DARPA网站2019年3月&#xff0c;美国防高级研究计划局&#xff08;DARPA&#xff09;“终身学习机器”&#xff08;L2M&#xff09;项目研究人员在《自然机器智能》杂志发表了其有关人工智能算法的研究结果&#xff0c;介绍了一种由类似动物肌腱驱动的人工智能…

php内核一 一次请求与结束

php开始 到 结束 有两个阶段 请求开始之间的初始化阶段  请求之后的结束处理阶段开始阶段&#xff1a; 模块初始化 模块激活模块初始化&#xff1a; 在整个SAPI生命周期内&#xff0c;只执行一次&#xff08;apache服务器启动的整个声明周期内或者是命令…

运行Qt程序的一些注意事项

一、脱离开发环境运行Qt程序的话&#xff0c;会出现丢失动态链接库的现 象。解决办法有以下两种&#xff1a; 1、把提示丢失的链接库添加到相应的应用程序下即可。&#xff08;比较麻烦&#xff09; 2、把F:\QT\5.7\mingw53_32\bin添加到系统的环境变量中去。&#xff08;最简…

《自然》,工程学突破!仿生物细胞群体机器人问世

来源&#xff1a;科技日报摘要&#xff1a;北京3月20日&#xff0c;英国《自然》杂志20日发表了一项工程学最新突破&#xff1a;美国科学家团队研发了一种能模拟生物细胞集体迁移的机器人&#xff0c;可实现移动、搬运物体及向光刺激移动。北京3月20日&#xff0c;英国《自然》…

【原创】我所理解的自动更新-外网web服务器配置

ClientDownload和ClientUpdate共享渠道配置信息: channel-0.php //以appstore的渠道为例 1 <?php 2 define(APPNAME, TOKENAPPNAME);//应用版本号3 define(SERVER_MAJOR_VERSION, TOKENMAJORVER);//最新应用版本号4 define(SERVER_MAJOR_VERSION_TXT, TOKEN…

白宫启动AI.GOV计划,呼吁各界携手共同推进AI发展

来源&#xff1a;网络大数据摘要&#xff1a;近日&#xff0c;白宫启动了 ai.gov 计划&#xff0c;列出了特朗普政府与美国联邦机构采取的一系列人工智能举措&#xff0c;如美国国立卫生研究院(NIH)利用 AI 展开的生物医学研究项目以及美国交通部近期发布的关于自动驾驶汽车的报…

Qt连接MySQL数据库

1、将MySQL安装目录下的libmysql.dll拷贝到Qt安装目录下的bin目录中。 2、准备数据库和数据表如下&#xff1a; 3、编写如下代码&#xff1a; #------------------------------------------------- # # Project created by QtCreator 2016-07-15T17:56:50 # #----------------…

关于手机权限

adb shell cd进入手机/data目录总显示opendir failed&#xff0c;permission denied 可能的原因有两个 1.手机没有root2.adb不是最高权限启动 针对1&#xff0c;PC上下载了一个KingRoot&#xff0c;可以root基本上的常用的手机 2.adb shell 后 su命令即可以在最高权限下运行 ad…

边缘计算不再“边缘”

来源&#xff1a;中国科学报摘要&#xff1a;5G商用时代来临&#xff0c;数据量将更加巨大、复杂&#xff0c;对计算提出更高要求&#xff0c;同时也为发展人工智能、边缘计算带来了新机遇。5G商用时代来临&#xff0c;数据量将更加巨大、复杂&#xff0c;对计算提出更高要求&a…

Qt中修改应用程序和标题栏的图标

一、修改应用程序图标 1.新建一个my.txt文件&#xff0c;打开后在其中加一句 “IDI_ICON1 ICON DISCARDABLE "应用程 序图标.ico"”。&#xff08;“应用程序图标.ico”是要添加的图片名&#xff0c;图片格式一定要是.ico), 然后保存并退出&#xff0c;将文件格式改为…

iOS一次定位解决方案(基于高德iOS SDK)

部分LBS应用&#xff0c;在打开app时获取当前的准确位置就能满足要求&#xff0c;一直开着定位&#xff0c;不仅会浪费流量也费电&#xff0c;所以这里给大家推荐一个一次定位的解决方法。 步骤如下&#xff1a; 1.开启定位&#xff0c;请求获取位置信息。&#xff08;注意&…

人类“第六感”首次被证实,研究发现人脑具有磁场感应能力

新证据表明&#xff0c;人类磁感可以让大脑感应到地球磁场来源&#xff1a;神经科技摘要&#xff1a;科学界已经知道鸟类可以利用地磁场进行导航&#xff0c;除此之外&#xff0c;科学家在自然界许多物种中都发现了磁感应能力&#xff0c;生物的磁感受能力也一直在业内被称作生…

用S-函数编写Simulink中的正弦模块

1、用S-函数实现一个正弦波信号源。要求其幅度、频率和初始相位参数可由外部设置&#xff0c;并将这个信号源进行封装。 S-函数程序代码如下&#xff1a; function [sys,x0,str,ts] ch2example17Sfun(t,x,u,flag,Amp,Freq,Phase) % 正弦波信号源 switch flag, case 0 …

fdsgds

public class Demo {public static void main(){//dfdsfdsfdsint i10; } }转载于:https://www.cnblogs.com/beyondbycyx/p/4391175.html

2019计算与系统神经科学大会Cosyne 前沿研究汇总

来源&#xff1a;混沌巡洋舰摘要&#xff1a;计算神经科学是一门超级跨学科的新兴学科&#xff0c;几乎综合信息科学&#xff0c;物理学&#xff0c; 数学&#xff0c;生物学&#xff0c;认知心理学等众多领域的最新成果。关注的是神经系统的可塑性与记忆&#xff0c;抑制神经元…

MATLAB中的S-Function的用法(C语言)

1. S-Function简介 S-Function是system-function的缩写。说得简单&#xff0c;S-Function就是用MATLAB所提供的模型不能完全满足用户&#xff0c;而提供给用户自己编写程序来满足自己要求模型的接口。 2. MEX函数与M文件的区别 第一&#xff0c; MEX 函数能实现的回调函数比…

npm国内镜像介绍

这个也是网上搜的&#xff0c;亲自试过&#xff0c;非常好用&#xff01; 镜像使用方法&#xff08;三种办法任意一种都能解决问题&#xff0c;建议使用第三种&#xff0c;将配置写死&#xff0c;下次用的时候配置还在&#xff09;: 1.通过config命令 npm config set registry …

一文读懂民航客机飞控系统

来源&#xff1a;传感器技术摘要&#xff1a;埃塞俄比亚航空公司波音737 MAX 8型客机当地时间10日坠毁&#xff0c;这是时隔不到5个月&#xff0c;波音同一型号飞机发生的第二起空难。鉴于两起事故具有明显的相似性&#xff0c;越来越多的将目标指向了该型号的设计缺陷——飞控…

VC创建DLL动态链接库及其调用

1.1 创建dll项目 1.2 为dll项目编写源文件 头文件dllDemo.hextern "C" _declspec(dllexport) int Sum(int a,int b);//加法函数。extern "C" _declspec(dllexport) int Max(int a, int b);//取较大值函数extern "C" _declspec(dllexport) int Mi…