2010-01-11 18 views
27

considerar lo siguiente:¿Los compiladores de C++ optimizan el paso por referencia de los parámetros de POD para pasar por copia?

struct Point {double x; double y;}; 

double complexComputation(const& Point p1, const Point& p2) 
{ 
    // p1 and p2 used frequently in computations 
} 

hacer compiladores optimizar el paso por referencia en el paso por copia para evitar desreferenciar frecuentes? En otras palabras se convierten en complexComputation esto:

double complexComputation(const& Point p1, const Point& p2) 
{ 
    double x1 = p1.x; double x2 = p2.x; 
    double y1 = p1.y; double y2 = p2.y; 
    // x1, x2, y1, y2 stored in registers and used frequently in computations 
} 

Desde el punto es una vaina, no puede haber ningún efecto secundario al hacer una copia a espaldas de la persona que llama, ¿verdad?

Si ese es el caso, entonces siempre puedo pasar objetos POD por referencia constante, sin importar cuán pequeño sea, y no tener que preocuparme por la semántica óptima para pasar. ¿Derecha?

EDITAR: Estoy interesado en el compilador de GCC en particular. Supongo que tendré que escribir un código de prueba y mirar el ASM.

+0

Intenté buscar esta pregunta, pero seguí encontrando aciertos sobre los abc's de pass-by-value, pass-by-reference, etc. Lo siento si ya se ha discutido. –

+3

Normalmente, lo contrario puede ser un mejor enfoque (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/). Pase por valor y deje que el compilador lo convierta en pase por referencia cuando lo desee. – jalf

+0

+1 Buena pregunta –

Respuesta

4

No puedo hablar por cada compilador, pero la respuesta general es no. No hará esa optimización.

Consulte GOTW#81 para leer acerca de cómo fundir a const en C++ no afecta la optimización, como algunos podrían pensar.

7

Su compilador puede absolutamente levantar Señalar las variables de los miembros en los registros si es necesario. Esto, sin embargo, no es lo mismo que el compilador convirtiendo la llamada a la función en un valor de pase.

Debe inspeccionar el ensamblaje generado para ver qué optimizaciones se están realizando.

Y FWIW, la regla general que uso es pasar todos los tipos de primative por valor y todas las clases/UDT (PODs o no) por referencia de referencia cuando puedo, y dejar que el compilador resuelva lo mejor que puede hacer. No deberíamos preocuparnos por los detalles de lo que hace el compilador, es mucho más inteligente que nosotros.

+2

Acepto no preocuparme, a menos que se lo indiquen los parámetros de evaluación/perfil. Pero estaba curioso si un compilador realmente puede hacer ese tipo de optimización. –

5

Hay 2 problemas.

En primer lugar, el compilador pasar-por-ref no convertido al pasar por valor, especialmente si complexComputation no es static (es decir pueden ser utilizados por los objetos externos).

El motivo es la compatibilidad API. Para la CPU, no existe una "referencia". El compilador convertirá las referencias a los punteros. Los parámetros se pasan en la pila o por medio de registro, por lo que un código de llamada complexComputation probablemente serán llamados como (asumir double es de longitud 4 por un momento):

str x1, [r7, #0x20] 
str y1, [r7, #0x24] 
str x2, [r7, #0x50] 
str y2, [r7, #0x54] 
push r7, #0x20  ; push address of p1 onto the stack 
push r7, #0x50  ; push address of p2 onto the stack 
call complexComputation 

Sólo 8 bytes se insertan en la pila.

paso por copia, por el contrario, va a empujar toda la estructura en la pila, por lo que el código de montaje se verá como

push x1 ; push a copy of p1.x onto the stack 
push y1 ; push a copy of p1.y onto the stack 
push x2 ; push a copy of p2.x onto the stack 
push y2 ; push a copy of p2.y onto the stack 
call complexComputation 

Tenga en cuenta que en esta ocasión 16 bytes se insertan en la pila, y el contenido son los números, no los punteros. Si el complexComputation cambia su semántica de paso de parámetros, la entrada se convertirá en basura y su programa puede bloquearse.


Por otro lado, la optimización

double complexComputation(const Point& p1, const Point& p2) { 
    double x1 = p1.x; double x2 = p2.x; 
    double y1 = p1.y; double y2 = p2.y; 
    // x1, x2, y1, y2 stored in registers and used frequently in computations 
} 

se puede hacer fácilmente, ya que el compilador puede reconocer qué variables se utilizan muy a menudo y para ser almacenados en registros reservados (por ejemplo, R4 ~ R13 la arquitectura ARM, y muchos de los registros sXX/dXX) para un acceso más rápido.


Después de todo, si usted quiere saber si un compilador ha hecho algo, siempre se puede desmontar los objetos resultantes y comparar.

Cuestiones relacionadas