2010-04-15 9 views
9

Quizás esto sea algo específico del compilador. Si es así, ¿qué tal para gcc (g ++)? Si se utiliza una referencia variable/alias de la siguiente manera:¿El costo de tiempo de ejecución de las referencias variables (alias) es incurable?

int x = 5; 
int& y = x; 
y += 10; 

es lo que realmente requiere más ciclos que si no usamos la referencia.

int x = 5; 
x += 10; 

En otras palabras, ¿cambia el código de la máquina, o el "alias" ocurre solo al nivel del compilador?

Esto puede parecer una pregunta tonta, pero tengo curiosidad. Especialmente en el caso en que tal vez sería conveniente cambiar el nombre temporalmente de algunas variables miembro para que el código matemático sea un poco más fácil de leer. Claro, no estamos hablando exactamente de un cuello de botella aquí ... pero es algo que estoy haciendo y entonces me pregunto si hay alguna diferencia "real" ... o si es solo cosmética.

Respuesta

9

Puede tratarse como un alias, pero no en términos de eficacia. Debajo del capó, una referencia es un puntero con una sintaxis más agradable y mayores garantías de seguridad. Por lo tanto, tiene una penalización de tiempo de ejecución de operación de "desreferencia". A MENOS QUE el compilador lo optimice, pero no contaría con eso por lo general.

En el caso de la cuestión de si el compilador optimizará o no, no hay otra manera de mirar el ensamblado generado.

+0

Buena respuesta. Gracias – cheshirekow

+4

La respuesta no es exactamente exacta. Una de las intenciones detrás de las referencias es implementar el concepto de un alias, nombre alternativo para un objeto existente. Creo que esto se afirma explícitamente en TC++ PL. Si bien este no es siempre el caso, "alias" sigue siendo una descripción precisa de las referencias en muchos casos. – AnT

+0

@AndreyT, nunca escuché la idea de que las referencias sean alias, ¿me puede decir el párrafo del estándar donde eso está implícito? –

4

La única manera de saber con certeza es compilar y examinar la salida del compilador. En general, la sobrecarga para una referencia es similar a la sobrecarga para un puntero, porque los punteros son generalmente cómo se implementan las referencias. Sin embargo, dado el caso simple que está mostrando, creo que la referencia se optimizaría.

+0

Sí, algún día podría hacer algunas pruebas simples y ver si se optimiza ... por ahora, no es demasiado importante. – cheshirekow

0

Sí, dereferencing el puntero detrás de la referencia de sufragar gastos adicionales de tiempo de ejecución, pero es probable insignificante. Escriba el código de la forma más clara posible para leer y articula de forma más clara la semántica que está buscando, y luego ejecute un generador de perfiles si el rendimiento es un problema (el cuello de botella rara vez es lo que usted supone). Si está en MacOS, Shark es fantástico.

+0

Hecho. Como dije, solo curiosidad. Desarrollar en Mac? ew ...;) – cheshirekow

+0

¡No desaire lo que no sabe! :-) Las herramientas de depuración y creación de perfiles son de primera clase y no cuestan $$$. – metasim

+0

Esta respuesta es incorrecta. –

4

Es cierto que en la mayoría de los casos, las referencias poner en práctica el concepto de "alias", un nombre alternativo para el objeto al que están unidos.

Sin embargo, en general, las referencias se implementan a través de punteros. Sin embargo, un buen compilador solo usará un puntero real para implementar la referencia en situaciones en las que el enlace real se determina en tiempo de ejecución. Si el enlace se conoce en tiempo de compilación (y los tipos coinciden), el compilador normalmente implementará la referencia como un nombre alternativo para el mismo objeto, en cuyo caso no habrá penalización de rendimiento para acceder al objeto a través de la referencia (en comparación con el acceso a través de su nombre original).

Su ejemplo es uno de aquellos en los que se debe esperar ninguna penalización en el rendimiento de la referencia.

4

Ambas funciones compilan exactamente el mismo código en g++, incluso usando -O1. (He añadido la declaración return para asegurar que el cálculo no se elimina por completo.)

hay ningún puntero, sólo una referencia. En este ejemplo trivial no hubo diferencia de rendimiento. Sin embargo, eso no garantiza que este sea siempre el caso (sin diferencia de rendimiento) para todos los usos de referencia.

int f() 
{ 
    int x = 5; 
    x += 10; 
    return x; 
} 

.

int f() 
{ 
    int x = 5; 
    int & y = x; 
    y += 10; 
    return y; 
} 

ensamblador:

movl $15, %eax 
ret 
+1

No estoy seguro de que esta sea una prueba justa, porque como muestran los resultados, el cuerpo entero de la función se puede evaluar en tiempo de compilación. –

+1

@Daniel Pryden: Pero ese es el código sobre el que se preguntó. ¿Puedes sugerir una prueba más justa del código? –

+0

Creo que mi prueba fue un poco más justa, porque el compilador no evaluó el cuerpo en tiempo de compilación. (Pero no probé el mismo código). –

2

que compara 2 programas en GNU/Linux. Solo la salida de GCC se muestra a continuación, pero los resultados de clang conducen a conclusiones idénticas.

versión de GCC: Versión4.9.2

Clang:3.4.2

Los programas

1.cpp

#include <stdio.h> 
int main() 
{ 
    int x = 3; 
    printf("%d\n", x); 
    return 0; 
} 

2.cpp

#include <stdio.h> 
int main() 
{ 
    int x = 3; 
    int & y = x; 
    printf("%d\n", y); 
    return 0; 
} 

La prueba

Intento 1: No hay optimizaciones

gcc -S --std=c++11 1.cpp

gcc -S --std=c++11 2.cpp

conjunto resultante de 1.cpp fue más corta.

Intento 2: Optimizaciones en

gcc -S -O2 --std=c++11 1.cpp

gcc -S -O2 --std=c++11 2.cpp

El conjunto resultante fue completamente idéntica.

La salida de montaje

1.cpp, ninguna optimización

.file "1.cpp" 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    subq $16, %rsp 
    movl $3, -4(%rbp) 
    movl -4(%rbp), %eax 
    movl %eax, %esi 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    movl $0, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

2.cpp, ninguna optimización

.file "2.cpp" 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    subq $16, %rsp 
    movl $3, -12(%rbp) 
    leaq -12(%rbp), %rax 
    movq %rax, -8(%rbp) 
    movq -8(%rbp), %rax 
    movl (%rax), %eax 
    movl %eax, %esi 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    movl $0, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

1.cpp, con optimizaciones

.file "1.cpp" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%d\n" 
    .section .text.unlikely,"ax",@progbits 
.LCOLDB1: 
    .section .text.startup,"ax",@progbits 
.LHOTB1: 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB12: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, %esi 
    movl $.LC0, %edi 
    xorl %eax, %eax 
    call printf 
    xorl %eax, %eax 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE12: 
    .size main, .-main 
    .section .text.unlikely 
.LCOLDE1: 
    .section .text.startup 
.LHOTE1: 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

2.CPP, con optimizaciones

.file "1.cpp" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%d\n" 
    .section .text.unlikely,"ax",@progbits 
.LCOLDB1: 
    .section .text.startup,"ax",@progbits 
.LHOTB1: 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB12: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, %esi 
    movl $.LC0, %edi 
    xorl %eax, %eax 
    call printf 
    xorl %eax, %eax 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE12: 
    .size main, .-main 
    .section .text.unlikely 
.LCOLDE1: 
    .section .text.startup 
.LHOTE1: 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

Conclusión

No hay ningún costo en tiempo de ejecución cuando se trata de la producción optimizada de GCC. Lo mismo ocurre con clang (probado con la versión 3.4.2): cuando las optimizaciones están activadas, el código ensamblador generado es idéntico en ambos programas.

Cuestiones relacionadas