Bietet das Schlüsselwort restrict signifikante Vorteile in gcc/g ++?
Es kann die Anzahl von Befehlen zu reduzieren, wie unten am Beispiel gezeigt, so benutzen, wann immer möglich.
GCC 4.8 Linux x86-64
Eingang 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;
}
Compile und decompile:
gcc -g -std=c99 -O0 -c main.c
objdump -S main.o
Mit -O0
, sie sind die gleichen.
Mit -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)
Für die Laien der calling convention ist:
rdi
= erste Parameter
rsi
= zweite Parameter
rdx
= dritte Parameter
Fazit: 3 Anweisungen statt 4.
Natürlich, Anweisungen can have different latencies, aber das ist eine gute Idee.
Warum konnte GCC das optimieren?
Der obige Code stammt aus der Wikipedia example, die sehr leuchtet.
Pseudo Anordnung für 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
Für 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
Ist es wirklich schneller?
ermmm ... nicht für diesen einfachen Test:
.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
Und dann:
as -o a.o a.S && ld a.o && time ./a.out
auf Ubuntu 14.04 AMD64 CPU Intel i5-3210M.
Ich gestehe, dass ich immer noch moderne CPUs nicht verstehe.Lassen Sie uns wissen, wenn Sie:
- einen Fehler in meiner Methode gefunden
- Assembler Testfall gefunden, wo es viel schneller
- wird verstehen, warum es keinen Unterschied
war
Aliasing Probleme sind wird häufig als der Hauptgrund angesehen, warum C/C++ bei vielen Rechenaufgaben weniger effizient ist als Fortran. Ich würde also sagen, dass jede Funktion, die Aliasing vermeidet, einen großen Unterschied machen kann. – jalf
mögliche Duplikate von [Realistische Verwendung des Schlüsselwortes C99 'restrict'?] (Http://stackoverflow.com/questions/745870/realistic-usage-of-the-c99-restrict-keyword) –