C/C++语言中的restrict关键字

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

使用此关键字,将允许编译器预知多个指针之间的相互独立性,从而允许编译器做更加激进的优化。例如[1]:

int add(int *a, int *b){
  *a = 10;
  *b = 12;
  return *a + *b;
}

在这个函数中,有的同学就说了,直接返回22不就好了,这么想的同学就忽略了a==b的可能,如果a和b指向同一个地址,那么这个函数将返回24。实际上,编译器就是这个想要把额外操作去掉的“同学”,如果我们能清晰的告诉他,a和b在本函数中不管怎么移动都永远不可能指向相同的地址,那么,处理这个函数时,*a + *b 可以直接使用两立即数相加,不需要再从地址中读取值,就能优化代码执行的效率。

因此,在没有指明a不可能等于b的情况下,编译器能做到的最大优化就是,b指向地址的值一定是12,但a的值必须从地址中读取。

-O3
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

上面400a1c地址指令,读a。

于是我们可以使用restrict关键字告诉编译器,你放心优化,传入的指针a和指针b一定不指向同一个地址。

int add(int __restrict *a, int __restrict *b){
  *a = 10;
  *b = 12;
  return *a + *b;
}

那么编译器就顿悟了,知道a中的内容一定是10,b中的内容一定是12,a也不需要读。

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                      retq

于是编译后的代码直接采用立即数寻址的方式完成这个函数。整体上又比上一次优化中,少了一条指令。别看仅仅少了一条指令,在性能上,我们将两者分别执行1亿次,后者仅耗费了56ns,而前者耗费了146ns [1],性能上接近为后者的1/3。

[1] https://zhuanlan.zhihu.com/p/349726808

你可能感兴趣的:(c语言,c++,java,开发语言)