代码之家  ›  专栏  ›  技术社区  ›  Robert S. Barnes Antoni

restrict关键字在gcc/g++中是否提供了显著的好处?

  •  44
  • Robert S. Barnes Antoni  · 技术社区  · 15 年前

    有没有人看到过关于C/C++是否使用的数字/分析 restrict gcc/g++actual中的关键字在现实中(而不仅仅是理论上)提供了显著的性能提升?

    我读过各种各样的文章,推荐/贬低它的使用,但我没有遇到任何真实的数字,实际上显示了双方的论点。

    编辑

    我知道 限制 不是C++的正式部分,但它是由一些编译器支持的,我已经阅读了一篇论文。 Christer Ericson 强烈建议使用。

    5 回复  |  直到 6 年前
        1
  •  45
  •   Georg Schölly Crazy Developer    15 年前

    restrict关键字有区别。

    在某些情况下(图像处理),我已经看到因子2和更多的改进。但大多数情况下,差异并不大。大约10%。

    这里有一个小例子来说明这个区别。我写了一个非常基本的4x4矢量*矩阵变换作为测试。注意,我必须强制函数不被内联。否则,gcc会检测到我的基准代码中没有任何别名指针,而restrict不会因为内联而有所不同。

    我也可以将转换函数移到其他文件中。

    #include <math.h>
    
    #ifdef USE_RESTRICT
    #else
    #define __restrict
    #endif
    
    
    void transform (float * __restrict dest, float * __restrict src, 
                    float * __restrict matrix, int n) __attribute__ ((noinline));
    
    void transform (float * __restrict dest, float * __restrict src, 
                    float * __restrict matrix, int n)
    {
      int i;
    
      // simple transform loop.
    
      // written with aliasing in mind. dest, src and matrix 
      // are potentially aliasing, so the compiler is forced to reload
      // the values of matrix and src for each iteration.
    
      for (i=0; i<n; i++)
      {
        dest[0] = src[0] * matrix[0] + src[1] * matrix[1] + 
                  src[2] * matrix[2] + src[3] * matrix[3];
    
        dest[1] = src[0] * matrix[4] + src[1] * matrix[5] + 
                  src[2] * matrix[6] + src[3] * matrix[7];
    
        dest[2] = src[0] * matrix[8] + src[1] * matrix[9] + 
                  src[2] * matrix[10] + src[3] * matrix[11];
    
        dest[3] = src[0] * matrix[12] + src[1] * matrix[13] + 
                  src[2] * matrix[14] + src[3] * matrix[15];
    
        src  += 4;
        dest += 4;
      }
    }
    
    float srcdata[4*10000];
    float dstdata[4*10000];
    
    int main (int argc, char**args)
    {
      int i,j;
      float matrix[16];
    
      // init all source-data, so we don't get NANs  
      for (i=0; i<16; i++)   matrix[i] = 1;
      for (i=0; i<4*10000; i++) srcdata[i] = i;
    
      // do a bunch of tests for benchmarking. 
      for (j=0; j<10000; j++)
        transform (dstdata, srcdata, matrix, 10000);
    }
    

    结果:(在我的2 GHz双核上)

    nils@doofnase:~$ gcc -O3 test.c
    nils@doofnase:~$ time ./a.out
    
    real    0m2.517s
    user    0m2.516s
    sys     0m0.004s
    
    nils@doofnase:~$ gcc -O3 -DUSE_RESTRICT test.c
    nils@doofnase:~$ time ./a.out
    
    real    0m2.034s
    user    0m2.028s
    sys     0m0.000s
    

    在拇指上,执行速度快20%,打开 那个 系统。

    为了显示它在多大程度上取决于架构,我让相同的代码在Cortex-A8嵌入式CPU上运行(由于我不想等那么长时间,所以稍微调整了循环计数):

    root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp test.c
    root@beagleboard:~# time ./a.out
    
    real    0m 7.64s
    user    0m 7.62s
    sys     0m 0.00s
    
    root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -DUSE_RESTRICT test.c 
    root@beagleboard:~# time ./a.out
    
    real    0m 7.00s
    user    0m 6.98s
    sys     0m 0.00s
    

    这里的差异只有9%(同样的编译器btw)。

        2
  •  7
  •   Community CDub    8 年前

    restrict关键字在gcc/g++中是否提供了显著的好处?

    可以 减少下面示例中所示的指令数量,因此尽可能使用它。

    GCC 4.8 Linux x86-64 exmample

    输入:

    void f(int *a, int *b, int *x) {
      *a += *x;
      *b += *x;
    }
    
    void fr(int *restrict a, int *restrict b, int *restrict x) {
      *a += *x;
      *b += *x;
    }
    

    编译和反编译:

    gcc -g -std=c99 -O0 -c main.c
    objdump -S main.o
    

    -O0 它们是一样的。

    -O3 :

    void f(int *a, int *b, int *x) {
        *a += *x;
       0:   8b 02                   mov    (%rdx),%eax
       2:   01 07                   add    %eax,(%rdi)
        *b += *x;
       4:   8b 02                   mov    (%rdx),%eax
       6:   01 06                   add    %eax,(%rsi)  
    
    void fr(int *restrict a, int *restrict b, int *restrict x) {
        *a += *x;
      10:   8b 02                   mov    (%rdx),%eax
      12:   01 07                   add    %eax,(%rdi)
        *b += *x;
      14:   01 06                   add    %eax,(%rsi) 
    

    对于没有经验的人来说, calling convention 是:

    • rdi =第一个参数
    • rsi =第二个参数
    • rdx =第三个参数

    结论: 3条指令而不是4条指令 .

    当然,指示 can have different latencies 但这是个好主意。

    为什么海湾合作委员会能够优化这一点?

    上面的代码取自 Wikipedia example 哪个是 非常 照明。

    的伪程序集 f :

    load R1 ← *x    ; Load the value of x pointer
    load R2 ← *a    ; Load the value of a pointer
    add R2 += R1    ; Perform Addition
    set R2 → *a     ; Update the value of a pointer
    ; Similarly for b, note that x is loaded twice,
    ; because a may be equal to x.
    load R1 ← *x
    load R2 ← *b
    add R2 += R1
    set R2 → *b
    

    为了 fr :

    load R1 ← *x
    load R2 ← *a
    add R2 += R1
    set R2 → *a
    ; Note that x is not reloaded,
    ; because the compiler knows it is unchanged
    ; load R1 ← *x
    load R2 ← *b
    add R2 += R1
    set R2 → *b
    

    真的更快吗?

    嗯…不是为了这个简单的测试:

    .text
        .global _start
        _start:
            mov $0x10000000, %rbx
            mov $x, %rdx
            mov $x, %rdi
            mov $x, %rsi
        loop:
            # START of interesting block
            mov (%rdx),%eax
            add %eax,(%rdi)
            mov (%rdx),%eax # Comment out this line.
            add %eax,(%rsi)
            # END ------------------------
            dec %rbx
            cmp $0, %rbx
            jnz loop
            mov $60, %rax
            mov $0, %rdi
            syscall
    .data
        x:
            .int 0
    

    然后:

    as -o a.o a.S && ld a.o && time ./a.out
    

    在Ubuntu 14.04 AMD64 CPU Intel I5-3210M上。

    我承认我仍然不了解现代CPU。如果你:

    • 在我的方法中发现了一个缺陷
    • 找到一个汇编程序测试用例,它在那里变得更快
    • 明白为什么没有区别
        3
  •  6
  •   unthought    12 年前

    文章 Demystifying The Restrict Keyword 指的是报纸 Why Programmer-specified Aliasing is a Bad Idea (pdf)这表示它通常没有帮助,并提供了支持这一点的度量。

        4
  •  0
  •   raphaelr    15 年前

    我测试过 this C程序。没有 restrict 用了12.640秒完成 限制 12.516。看起来像 可以 节约 一些 时间。

        5
  •  0
  •   Clifford    15 年前

    请注意,允许C++的编译器 restrict 关键字可能仍然忽略它。例如,就是这样 here .