制限キーワードはgcc/g ++で大きな利点を提供しますか?
以下の例のように命令数を減らすことができるので、可能な限り使用してください。
GCC 4.8Linuxx86-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)
初心者の場合、呼び出し規約は次のとおりです。
rdi
=最初のパラメータ
rsi
=2番目のパラメーター
rdx
=3番目のパラメーター
結論:4ではなく3つの命令。
もちろん、命令のレイテンシーは異なる場合がありますが、これは良い考えです。
なぜGCCはそれを最適化できたのですか?
上記のコードは、非常に明るいウィキペディアの例から取られたものです。
の疑似アセンブリ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
本当に速いですか?
えーと...この簡単なテストではありません:
.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 CPUInteli5-3210Mの場合。
私はまだ現代のCPUを理解していないことを告白します。次の場合はお知らせください。
- 私の方法に欠陥を見つけました
- それがはるかに速くなるアセンブラテストケースを見つけました
- 違いがなかった理由を理解する