C/C++关键字之restrict

C/C++关键字之restrict

在C语言中,restrict关键字用于修饰指针(C99标准)。通过加上restrict关键字,程序员可提示编译器:在该指针的生命周期内,其指向的对象不会被别的指针所引用。

需要注意的是,在C++中,并无明确统一的标准支持restrict关键字。但是很多编译器实现了功能相同的关键字,例如gcc和clang中的__restrict关键字。

1
2
3
4
5
6
int add1(int*a, int* b)
{
*a = 10;
*b = 12;
return *a + *b;
}

add1函数的返回值会永远是10 + 12 = 22吗?

答案是不一定。在指针a和b的地址不同时,返回22没有问题。但是当指针a与b指向的是同一个int对象时,该对象先被赋值为10,后被赋值为12,因此a和b都返回12,因此add1函数最终返回24。

下面是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
int add1(int* a, int* b)
{
*a = 10;
*b = 12;
return *a + *b;
}
int main(){
int* c;
int* d;
d=(int*)malloc(sizeof(int)*1);
c=(int*)malloc(sizeof(int)*1);
*c=10;
*d=12;
*d=12;
cout<<"c:"<<*c<<endl;
cout<<"d:"<<*d<<endl;
cout<<"When Pointers a and b have different addresses"<<endl;
cout<<add1(c,d)<<endl;
cout<<"When Pointers a and b have the same address"<<endl;
cout<<add1(c,c)<<endl;
return 0;
}

程序输出:

1
2
3
4
5
6
c:10
d:12
When Pointers a and b have different addresses
22
When Pointers a and b have the same address
24

开启-O3优化,add1对应的汇编代码如下:

为了得到*a的值访问了1次内存,而不管在何种条件下(a == b or a != b),*b的值都是12。因此编译器将*a的值载入eax寄存器后,直接加上立即数12,而无需再访问内存获取*b的值。在无法确定指针a和b是否相同的情况下,编译器只能帮你优化到这里了。

1
2
3
4
5
6
0000000000400a10 <_Z4add1PiS_>:
400a10: c7 07 0a 00 00 00 movl $0xa,(%rdi) ; *a = 10
400a16: c7 06 0c 00 00 00 movl $0xc,(%rsi) ; *b = 10
400a1c: 8b 07 mov (%rdi),%eax ; 结果 = *a
400a1e: 83 c0 0c add $0xc,%eax ; 结果 += 12
400a21: c3 retq

加上了restrict关键字过后,同样开启-O3优化,add1对应的汇编代码如下:

加上关键字restrict后,编译器能够确认指针a和b不可能指向同一个内存地址,因此在求*a + *b时,无虚访问内存,因为*a必然等于立即数10,*b必然等于立即数12。

1
2
3
4
5
6
7
8
9
10
11
int add2(int* __restrict  a, int* __restrict b) 
{
*a = 10;
*b = 12;
return *a + *b ;
}
0000000000400a30 <_Z4add2PiS_>:
400a30: c7 07 0a 00 00 00 movl $0xa,(%rdi) ; *a = 10
400a36: b8 16 00 00 00 mov $0x16,%eax ; 结果 = 22
400a3b: c7 06 0c 00 00 00 movl $0xc,(%rsi) ; *b = 12
400a41: c3

有无restrict关键字的两种情况下的汇编指令可看到,后者比前者少访问一次内存,且少执行一条指令。就是因为没加restruct关键字时,编译器不能确定别的地方是不是会修改此值,所以会去相应的地址查看。

这样当我们明确知道两个指针不可能指向同一个地址时,我们就可以通过使用restrict关键字来进行性能优化。

注意使用restrict的时候,程序员必须确保不会出现pointer aliasing,即同一块内存无法通过两个或以上的指针变量名访问。不满足这个条件强行指定restrict,将会出现underfined behavior

通常编写代码时会忽略pointer aliasing的问题。更常见是在性能分析时,通过反汇编看到很多冗余的读取指令,才会想到加入restrict关键字来提升性能。

参考资料

C/C++关键字之restrict - 知乎 (zhihu.com)

Improve performance of aggregate functions by alexey-milovidov · Pull Request #19946 · ClickHouse/ClickHouse (github.com)

restrict type qualifier - cppreference.com

如何理解C语言关键字restrict? - 知乎 (zhihu.com)

(65条消息) 关键字_restrict___restrict_楚楚可薇的博客-CSDN博客

(65条消息) 【C++】关键字restrict的作用_restrict关键字的作用_不知所措的渣渣辉的博客-CSDN博客


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!