restrict 关键字在 gcc / g++ 中是否提供了显着的好处?
它可以减少如下示例所示的指令数量,因此请尽可能使用它。
GCC 4.8 Linux x86-64 示例
输入:
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,但这提供了一个好主意。
为什么 GCC 能够对其进行优化?
上面的代码取自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 x may point to a (a aliased by x) thus
; the value of x will change when the value of a
; changes.
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" is no longer needed.
load R2 ← *b
add R2 += R1
set R2 → *b
真的更快吗?
Ermmm...不适合这个简单的测试:
.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。让我知道你是否:
- 发现我的方法有缺陷
- 找到了一个汇编程序测试用例,它变得更快
- 了解为什么没有区别