727 字
4 分钟
C/C++中restrict关键词的用法

在阅读 Nvidia 的技术博客的过程中,发现了这么一段函数:

void saxpy(int n, float a, float * restrict x, float * restrict y)
{
  for (int i = 0; i < n; ++i)
      y[i] = a*x[i] + y[i];
}

此前没有见过restrict这种关键词的写法,于是去查了一下,发现这个关键词主要是对编译器生成的代码做优化的,假设我们有这样的一段函数:

void func(int * sum, int * num1, int * num2)
{
    *sum += *num1;
    *sum += *num2;
}

在 m2 芯片搭载的 macos 操作系统,使用 clang 19.1.7 编译器的情况下,启用-O3编译优化选项,并去除一些无关的符号后,得到的汇编指令如下:

ldr w8, [x1]    # num1的值加载到w8寄存器
ldr w9, [x0]    # sum的值加载到w9寄存器
add w8, w9, w8  # w8与w9寄存器的值相加并将结果放入w8寄存器
str w8, [x0]    # w8的值存入sum指针指向的地址
ldr w9, [x2]    # num2的值加载到w9寄存器
add w8, w9, w8
str w8, [x0]    # w8的值存入sum指针指向的地址

可以看到,编译器生成的代码,在执行源代码*sum += *num2之前执行了str w8, [x0]指令,这是因为在编译器,我们无法得知sumnum2是否指向的是同一块内存区域,当这两个指针指向的是同一区域的时候,出于正确性的考虑,我们就需要在加载num2的值之前将寄存器的值写回对应的内存区域。而如果我们已经确切的知道sumnum2指向的是不同的内存区域,我们就可以通过restrict关键词来显式地告诉编译器这件事,从而让编译器做更多的优化,将源代码改为如下:

void func(int * restrict sum, int * num1, int * restrict num2)
{
    *sum += *num1;
    *sum += *num2;
}

重新编译,得到生成的汇编指令:

ldr w8, [x1]
ldr w9, [x0]
ldr w10, [x2]
add w8, w9, w8
add w8, w10, w8
str w8, [x0]

此时可以看到,编译器生成的汇编指令相较于此前减少了一条,且在指令执行的过程中无需等待str写回操作的完成,从而提升了代码的执行效率。在这里,restrict关键词的作用就是告诉编译器,该指针指向的内存地址没有被其他指针所指,因此可以更为激进地去优化代码,相当于是程序员人为保证了这件事情,而如果用了restrict但实际上存在多个指针指向同一块内存区域,就可能产生未定义行为。

c++中同样支持了类似于restrict的关键词:GCCClang编译器支持__restrict__以及__restrict关键词,它们的使用方式与crestrict关键词一致。

C/C++中restrict关键词的用法
https://0130w.github.io/posts/computer/cpp_restrict_keyword_usage/
作者
0130
发布于
2025-02-06
许可协议
CC BY-NC-SA 4.0