[转]Restrict关键字

0 定义

C99中新增加的用于修饰指针的关键字,用于表示该指针所指向的内存,只有通过该指针访问得到(如下ptr指向的内存单元只能通过ptr访问得到)。从而可以让编译器对代码进行优化,生成更有效率的汇编代码。

char *restrict ptr;

  

1 优化举例

举例1,如下代码(引自参考1),以及翻译成汇编之后的代码。

#include <stdio.h>

#ifdefRES
void multi_add(int* restrict p1, int* restrict p2, int* restrict pi)
#else
void multi_add(int* p1, int* p2, int* pi)
#endif

{
  *p1+= *pi;
  *p2+= *pi;
}

int main()
{
  int a =1, b = 2;
  int inc =1;
  // increase both aand b by 1
  multi_add(&a,&b, &inc);
  // print the result
  printf("a= %d, b = %d\n", a, b);

}

         调用mulit_add函数时,翻译成汇编后的代码,如果是没有RES,则是

mov(%rdx),%eax
add%eax, (%rdi)
mov(%rdx),%eax
add%eax, (%rsi)

        相反如果有RES定义,则是

mov(%rdx),%eax
add %eax,(%rdi)
add %eax,(%rsi)

  

因为pi是由restrict关键字修饰的,所以认为pi所指向的内存只可能通过pi访问,不可能其它 alias能访问到。所以没有必要每次都mov操作。

举例2

int ar[10];
int *restrict restar =(int *)malloc(10* sizeof(int));
int *par =ar;

for (n =0; n< 10; n++)
{
  par[n]+= 5;
  restar[n]+= 5;
  ar[n]*= 2;
  par[n]+= 3;
  restar[n]+= 3;
}

  

同样由于restar 是用restrict修饰的,所以编译器会优化成restar[n] += 8;

其实优化的本质上就是使用了一系列这寄存器,而传统架构上的使用Cache(可以参见https://www.zhihu.com/question/41653775)。

2 使用举例
       用restrict修饰的指针,最多的是用来作为参数、memcpy中的src和dst就是使用restrict修改的,所以个人总结出来的用途主要在于copy操作过程中,不用每个字节每个字节去取址计算操作,而是直接page操作的可能。大大提升了性能。

3 注意点
       首先,有可能反汇编后的代码,并不能达到遇期的优化效果,这是因为gcc中指定的优化等级不对,或者根本没有指定优化等级。所以为了让编译器识别并优化restrict关键字,必须编译时指定优化等级。如在1中的举例,如果multi_add(&a,&b,&a);调用,那么在不同等级优化下的效果,如下表所示。

优化等级  最终值 原因
不优化 a = 2; b = 4; Gcc在没有开-O优化时是不会对restrict关键字优化
-O1 A=2;b=3; restrict关键字的优化
-O2及以上 a=2;b=4; Multi_add函数不再被main调用

然后,restrict关键字,不仅是告诉编译器优化代码,而且还对使用者(程序员)提出了要求,即必须保证restrict指针指向的内存,只能通过该指针访问。(记住这点是要求程序员的,编译器无法识别出报warning之类的信息)。因此,如下的代码,都可能是有问题的。

float x[100];
float *c;
void f(int n, float *restrict a, float *const b){
  int i;
  for (i =0; i < n; i++)
    a[i]= b[i]+ c[i];
}

void g3(void){
  float d[100],e[100];
  c =x; f(100,d, e);// OK
  f(50,d, d +50); // OK
  f(99,d + 1, d);// undefined behavior
  c =d; f(99,d + 1, e);// undefined behavior
  f(99,e, d +1); //

         最后,restrict的指针不能赋值给另一个restrict的指针,如下。但是,restrict的指针可以赋值给一个非restrict的指针。

int* restrict p1 =&a;
int* restrict p2 =&b;
p1 =p2; //undefined behavio

void f(int n, float *restrict r, float *restrict s){
  float *p = r,*q = s; //OK
  while (n--> 0) *p++ = *q++;// almost certainly optimized just like *r++= *s++
}  

  

4 参考文献

[0] http://blog.csdn.net/nathan_wz/article/details/8274451

[1] http://en.cppreference.com/w/c/language/restrict

 

原文地址:https://www.cnblogs.com/wlzy/p/10547038.html

时间: 2024-10-12 15:44:27

[转]Restrict关键字的相关文章

c99标准的restrict关键字

参考自restrict restrict关键字出现于C99标准,wiki上的解释restrict from wiki. In the C programming language, as of the C99 standard, restrict is a keyword that can be used in pointer declarations. The restrict keyword is a declaration of intent given by the programmer

[转] restrict关键字用法

PS: 在函数中,指针参数指定了restrict,表示这个指针指向的这段区域只能通过这个指针修改 c99中新增加了一个类型定义,就是restrict. 看了下网上的相关贴子,但还是问题解决的不够.下面是相关一个文章,我将在后面再加相关说明:那么restrict的意义是什么呢? 概括的说,关键字restrict只用于限定指针:该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径:这样的后果是帮助编译器进行更好的代码优化,生成更

C语言restrict关键字的使用----可以用来优化代码

C99中新增加了restrict修饰的指针:由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取.对对象的存取都限定于基于由restrict修饰的指针表达式中. 由restrict修饰的指针主要用于函数形参,或指向由malloc()分配的内存空间.restrict数据类型不改变程序的语义.编译器能通过作出restrict修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程. [典型例子] memcpy()在C99中,re

C 语言restrict 关键字的概念及使用例子

restrict是c99标准引入的,它只可以用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式.即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码. C语言核心技术上有对它的详细应用: void *memcpy( void * restrict dest , const void * restrict src, size_t n) 这里给出一

DSP 中关键字extern,cregister,Near ,Far,restrict,volatile

extern:extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.另外,extern也可用来进行链接指定. const: 可以用const定义一些变量或数组,以确保它的值不被改变. 例:int *const p = &x;  定义一个常量指针p给变量int.       const int *q = &x;  定义一个变量指针q给常量int. cregister:定义用来直接访问CPU控制寄存器的变量. 你可以先定义

c/c++常用的几个关键字总结

一.volatile volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据.如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象.下面举例说明.在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:short flag;void test(){do1();while(flag==0);do2();} 这段程序

C语言之存储类的相关的关键字

不同的数据在内存中的存储位置是不同的,总体来说内存中存储数据的地方主要有四部分:栈.堆.数据段.bss段,这些地方分别存放着不同的数据,比如栈一般用来存储局部变量,堆内存需要程序员字自己申请以及释放,主要用来存放比较大的数据:数据段主要用来存放显示初始化的全局变量和static关键字修饰的静态局部变量:bss段一般用来存放未显式初始化的全局变量或显式初始化为0的全局变量(C语言中,默认全局变量初始化为0).C语言还提供了一些关键字来修饰变量,使其附有其他的属性,这些关键字主要有:auto.sta

C中的const,volatile与restrict

volatile表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份.它可以适用于基础类 型如:int,char,long......也适用于C的结构和C++的类.当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员 都会被视为volatile.该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程.简单示例:DWORD __stdcall th

GCC在C语言中内嵌汇编 asm __volatile__ 【转】

转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC会自动插入代码完成必要的操作. 1.简单的内嵌汇编 例: __asm__ __volatile__("hlt"); "__asm__"表示后面的