restrict关键字在gcc / g ++中提供了很大的好处
有没有人看到任何数字/分析是否在gcc / g ++实际中使用C / C ++ restrict
关键字在现实中(而不仅仅是在理论上)提供了显着的性能提升?
我读过各种推荐/贬低它的用法的文章,但我没有碰到任何真正的数字,实际上certificate双方的论点。
编辑
我知道restrict
并不是C ++的正式组成部分,但是它受到了一些编译器的支持,我读过了Christer Ericson的一篇文章,强烈build议使用它。
restrict关键字有所不同。
在某些情况下(image processing),我看到了因子2的改进。 大多数情况下,差异并不大。 大约10%。
这里是一个小例子,说明不同之处。 我已经写了一个非常基本的4x4vectormatrix变换作为testing。 请注意,我不得不强制function不内联。 否则,GCC会检测到我的基准代码中没有任何别名指针,并且由于内联限制不会产生影响。
我本可以将转换函数移到不同的文件中。
#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 Core Duo上)
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-A8embedded式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%(相同的编译器顺便说一句)。
解密限制关键字的文章指的是为什么程序员指定的别名是一个坏主意 (pdf),它说,它通常没有帮助,并提供测量来支持。
restrict关键字在gcc / g ++中提供了很大的好处吗?
它可以减less指令的数量,如下例所示,所以尽可能使用它。
GCC 4.8 Linux x86-64 exmample
input:
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)
对于外行来说, 调用约定是:
-
rdi
=第一个参数 -
rsi
=第二个参数 -
rdx
=第三个参数
结论: 3条指令而不是4条 。
当然,说明可以有不同的延迟 ,但这是一个好主意。
为什么GCC能够优化?
上面的代码取自维基百科的例子 , 非常有启发性。
伪assembly为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
它真的更快吗?
呃…不是为了这个简单的testing:
.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 ao aS && ld ao && time ./a.out
在Ubuntu 14.04 AMD64 CPU Intel i5-3210M上。
我承认我还不懂现代的CPU。 让我知道你是否:
- 在我的方法中发现了一个缺陷
- 发现一个汇编器testing用例,它变得更快
- 理解为什么没有区别
我testing了这个 C程序。 没有restrict
,完成了12.640秒, restrict
12.516。 看起来可以节省一些时间。
请注意,允许restrict
关键字的C ++编译器可能仍然会忽略它。 例如在这里就是这种情况。