¿La palabra clave restrict proporciona beneficios significativos en gcc/g ++?
Se puede reducir el número de instrucciones como se muestra en el ejemplo a continuación, a fin de utilizarlo siempre que sea posible.
GCC 4.8 Linux x86-64 exmample
de entrada:
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;
}
Compilar y descompilar:
gcc -g -std=c99 -O0 -c main.c
objdump -S main.o
Con -O0
, que son los mismos.
Con -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)
Para los no iniciados, la calling convention es:
rdi
= primer parámetro
rsi
= segundo parámetro
rdx
= tercer parámetro
Conclusión: 3 instrucciones en lugar de 4.
Por supuesto, las instrucciones can have different latencies, pero esto da una buena idea.
¿Por qué GCC pudo optimizar eso?
El código anterior fue tomado del Wikipedia example que es muy iluminador.
Pseudo montaje para 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
Para 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
¿Es realmente más rápido?
ermmm ... no por esta sencilla prueba:
.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
Y luego:
as -o a.o a.S && ld a.o && time ./a.out
en Ubuntu 14.04 AMD64 CPU Intel i5-3210M.
Confieso que todavía no entiendo las CPU modernas.Déjeme saber si usted:
- encontrado un fallo en mi método
- encontró un caso de prueba ensamblador donde se convierte en mucho más rápido
- entender por qué no había una diferencia
cuestiones son Aliasing comúnmente considerado el motivo # 1 por el cual C/C++ es menos eficiente en muchas tareas computacionales que Fortran. Entonces, diría que cualquier característica que ayude a evitar el aliasing puede marcar una * gran * diferencia. – jalf
posible duplicado de [Uso realista de la palabra clave 'restringir' C99?] (Http://stackoverflow.com/questions/745870/realistic-usage-of-the-c99-restrict-keyword) –