1601|0

3836

帖子

19

TA的资源

纯净的硅(中级)

楼主
 

关于编译器优化的两个类型限定词:volatile和restrict [复制链接]

想把学习过程中的一些心得记录下来,权当自己学习经历中的笔记吧。如果你无意中看到这些文章,能帮我指出其中一些理解不正确的地方,在这里小弟将万分感谢。呵呵。
       volatile和restrict这两个类型限定词的运用与编译器的优化存在着一定的关系。volatile这个关键字用在变量类型定义上,指明这个变量的值存在不确定因素。也就是说这个变量不光会被我们编写的程序改变值,也可能会被某个外部代理改变(比如:某个硬件中断、外部程序等)。这样就不能保证如果程序没有改变这个变量值,而又存在多次调用后进入寄存器中的值就一定正确。
       从编译器的优化角度,举个例子:
       int x=5;
       int a, b;
       a = x;
       b = x;
       由于程序没有更改X的值,但又存在多次调用,编译器为了优化运行速度,会给a赋值后,把X的值5从内存放入到寄存器中。当给b赋值时,不是再次读取X内存地址中的值,而是直接把寄存器中的5赋给b。这一优化对于普通变量没有问题。但如果定义成 volatile int x;则表明x可以被程序代码外的其他代理改变值。如果编译器也采用这样的优化,很可能在给b赋值时,x的值已经被程序外部的某个硬件中断改变了。这样从寄存器获取到的值肯定是不正确的。
       因此当给变量加上volatile关键字,除了表示这一变量可以被其他代理改变值,也明确说明编译器不能为此变量进行上面那种方式的优化:每次调用这一变量,都从变量的地址中获取值,而不是寄存器(此变量使用的硬件内存地址是与其他并行运行的程序共享数据的,因此不管是程序自身改变变量值,还是其他代理改变变量值,都是改变内存地址中的数据)。
      看个有趣的例子:
      int square(volatile int *a)
      {
           return (*a * *a);
      }
      函数的目的本来是计算平方根,但由于a指针用了volatile关键字,两次获取a指针地址中的值不能完全保证一样,所以计算出来的结果也未必就是我们需要的。考虑修改成这样:
      int square(volatile int *a)
      {
           int temp = *a;
           return (temp * temp);
      }

      restrict关键字只能用来修饰指针,表示被定义的指针是访问指针中数据的唯一途径。这一目的是告诉编译器可以进行一些优化。看个例子:
      int x = 2;
      int *a = (int *) malloc(sizeof(int));
      *a = 2;
      int *b = &x;
      *a += 2;
      *b += 2;
      x *= 3;
      *a += 3;
      *b += 3;
     编译器进行优化时可以用一条语句代替:*a += 5;这对于a来说是正确的,但如果用*b += 5来优化b是不正确的。因为其他变量影响了结果。因此,当编译器不确定某些因素时,会放弃寻找某个途径进行优化。如果在变量前加上restrict关键字。则告诉编译器可以“放心大胆”的进行优化。但编译器并不会验证你定义为restrict的指针,是否真正是某个数据的唯一访问途径;就像数组的下标越界一样,如果你不遵守规则,编译器并不会指出错误,但后果由你自己负责:)
     同样看个有趣的类子:
    void change_array(restrict int *array, const restrict int *value,const int size)
    {
           for(int i=0;i            {
                  array[i] += *value;
           }
    }

    int main(void)
    {
           int *array[SIZE]  = {1,2,3};

          change_array(array,&array[0],SIZE);

          for(int i=0;i           {
                printf("%d \n",array[i]);
         }
     }
     如果编译器支持优化,运行后的结果是:2   3   4   而不是实际正确的结果:2   4   5 。这是在定义函数时,指明两个指针为restrict,因此编译器进行优化了:在程序调用函数时,将value指针的变量值在寄存器中生成了一个副本。后面的执行都是获取寄存器上的value值。同时可以看出,当你没有遵守restrict定义的指针指向的变量只能通过该指针修改的规则时(函数中 value指针指向的数据,在main调用时,array指针也进行了修改),编译器不会检查。
    对于优化来说,volatile是强制性,而restrict是建议性。也就是加了volatile则强制不进行优化,而加入restrict编译器也不一定肯定优化。大部分情况下restrict和什么都不加编译结果相同,restrict只是告诉编译器可以自由地做一些相关优化的假定。同时也告诉调用者仅使用满足restrict定义条件的参数,如果你不遵守,嘿嘿。。。

     restrict这个关键字是C99标准加入,在C++中不支持,因此我在VC++中加入restrict关键字编译不了:(
     关于restrict的加入,在网上还找到一段小故事:
     为了提高 Cray机器上的效率, ANSI C委员会提出过一种称为noalias的机制来解决这个问题,用它来说明某个C指针可以认为是没有别名, 只是这种机制不成熟,这件事激怒了Dennis Ritchie,拿他对C的标准化过程做了唯一的一次干预。他写了一封公开信说“noalias必须靠边站,这一点是不能协商的。”  

      后来Cray的Mike Holly又抓起了这个难题,向数值C语言扩充工作组和C++委员会提出了一种改进的反别名建议。所建议的想法是允许程序员说明一个指针可以认为是没有别名的,采用的方式是将它说明为restrict。  这个建议C99采纳了,但标准C++拒绝了。

 
点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表