2011-09-22 9 views
7

He leído que el estándar de C++ permite la optimización a un punto en el que realmente puede obstaculizar con la funcionalidad esperada. Cuando digo esto, estoy hablando de la optimización del valor de retorno, donde en realidad podría tener algo de lógica en el constructor de copia, pero el compilador optimiza la llamada.Pregunta sobre la optimización en C++

Encuentro que esto es algo malo, como en alguien que no sabe esto podría pasar bastante tiempo arreglando un error resultante de esto.

Lo que quiero saber es si hay otras situaciones en las que la sobre-optimización del compilador puede cambiar la funcionalidad.

Por ejemplo, algo como:

int x = 1; 
x = 1; 
x = 1; 
x = 1; 

podría ser optimizado para una única x = 1;

Supongamos que tengo:

class A; 

A a = b; 
a = b; 
a = b; 

Podría esto también puede optimizar? Probablemente no sea el mejor ejemplo, pero espero que sepas lo que quiero decir ...

+4

No estoy de acuerdo con el primer voto. Esta es una pregunta real y contestable. –

+4

Cuando se eliminan los códigos de copiado, se produce un error en el código, entonces se diseñó el copiador incorrecto al principio. Su código no debería depender de cuántos objetos hay alrededor o de la frecuencia con la que se copian/asignan. – PlasmaHH

+1

La lógica del copiador debe ser lógica para copiar el objeto. Si no se copia, ¿por qué debería copiarse la lógica del controlador que se debe ejecutar? –

Respuesta

12

operaciones de copia elidiendo es el único caso en el que se permite que un compilador para optimizar hasta el punto en efectos secundarios cambian visiblemente. No confíe en que se llamen a los constructores de copia, el compilador podría optimizar esas llamadas.

Para todo lo demás, se aplica la regla "como si": el compilador puede optimizar a su antojo, siempre que los efectos secundarios visibles sean los mismos que si el compilador no hubiera sido optimizado en absoluto.

("efectos secundarios visibles" incluyen, por ejemplo, material escrito a la consola o el sistema de archivos, pero no tiempo de ejecución y la velocidad del ventilador de la CPU.)

+0

+1: "los efectos secundarios cambian visiblemente". Puede valer la pena expandirse en lo que es realmente un efecto secundario visible. –

+1

Me gustaría señalar que esto fue antes de que se introdujeran los valores r. Esto permitió optimizaciones. Ahora que existen valores r, esas optimizaciones probablemente no son tan relevantes (la elisión de copia es aún más rápida que una llamada al constructor de movimiento, pero no por el mismo margen), el comportamiento se mantuvo, no por compatibilidad con versiones anteriores, sino porque todavía proporciona un beneficio y la gente ha aprendido a no usar el constructor de copias para trucos de todos modos. –

+0

@Rob: ¿Eso sería suficiente para tu gusto?) – sbi

1

Esto dependerá de cómo se implementa class A, si el compilador puede ver la implementación y si es lo suficientemente inteligente. Por ejemplo, si operator=() en class A tiene algunos efectos secundarios, dicha optimización cambiaría el comportamiento del programa y no es posible.

3

Podría estar optimizado, sí. Pero todavía tiene cierto control sobre el proceso, por ejemplo, el código suponen:

int x = 1; 
x = 1; 
x = 1; 
x = 1; 
volatile int y = 1; 
y = 1; 
y = 1; 
y = 1; 

A condición de que ni x ni y se utilizan por debajo de este fragmento, VS 2010 genera código:

 
    int x = 1; 
    x = 1; 
    x = 1; 
    x = 1; 
    volatile int y = 1; 
010B1004 xor   eax,eax 
010B1006 inc   eax 
010B1007 mov   dword ptr [y],eax 
    y = 1; 
010B100A mov   dword ptr [y],eax 
    y = 1; 
010B100D mov   dword ptr [y],eax 
    y = 1; 
010B1010 mov   dword ptr [y],eax 

Eso es , la optimización elimina todas las líneas con "x" y deja las cuatro líneas con "y". Así es como volátil funciona, pero el punto es que usted todavía tiene control sobre qué compilador hace por usted.

Si se trata de una clase o un tipo primitivo, todo depende del compilador, qué tan sofisticados son los límites de optimización.

Otro fragmento de código para el estudio:

class A 
{ 
private: 
    int c; 

public: 
    A(int b) 
    { 
     *this = b; 
    } 
    A& operator = (int b) 
    { 
     c = b; 
     return *this; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int b = 0; 
    A a = b; 
    a = b; 
    a = b; 
    return 0; 
} 

Visual Studio 2010 tiras de optimización de todo el código que nada, en la liberación de construir con tmain "optimización completa" hace apenas nada y vuelve inmediatamente a cero.

-1

I no sabe C++ que mucho, pero actualmente estoy leyendo compiladores-Principios, técnicas y herramientas

aquí es un fragmento de su sección en la optimización de código:

la fase de código-optimización independiente de la máquina intenta mejorar el código intermedio para que se obtenga un mejor código objetivo. Por lo general, mejor significa más rápido, pero se pueden desear otros objetivos, como código más corto o código de destino que consume menos energía. por ejemplo, un algoritmo directo genera el código intermedio (1.3) usando una instrucción para cada operador en la representación en árbol que viene de del analizador semántico. un algoritmo simple de generación de código intermedio seguido de la optimización del código es una forma razonable de generar un código de objetivo bueno . el optimizar puede duduce que la conversión de 60 de entero a punto flotante se puede hacer una vez y para todo en tiempo de compilación, por lo que la operación inttofloat se puede eliminar reemplazando el número entero 6 por el número de coma flotante 60.0. t3 otra parte se utiliza sólo una vez para trasmitir su valor para ID1 por lo que el optimizador puede transformar 1.3 en la secuencia más corta (1,4)

1.3 
t1 - intoffloat(60 
t2 -- id3 * id1 
ts -- id2 + t2 
id1 t3 

1.4 
t1=id3 * 60.0 
id1 = id2 + t1 

todos y todo lo que quiere decir que la optimización del código debe ir en una un nivel mucho más profundo y porque el código está en un estado tan simple no tiene efecto lo que hace su código

0

La optimización no (en el término adecuado) "eliminar llamadas para copiar o asignaciones". Convierte una máquina de estado finito en otro estado finito, máquina con un comportamiento externo idéntico.

Ahora, si repeadly llama

a=b; a=b; a=b; 

lo que hace el compilador depende de lo que en realidad es operator=. Si el compilador encuentra que una llamada no tiene posibilidades de alterar el estado del programa (y el "estado del programa" es "todo lo que vive dura más que un alcance al que puede acceder un alcance") lo quitará. Si esto no se puede "demostrar", la llamada permanecerá en su lugar.

Lo que haga el compilador, no se preocupe demasiado por: el compilador no puede (por contrato) cambiar la lógica externa de un programa o parte de él.

-1

Tuve algunos problemas con las variables const y const_cast. El compilador produjo resultados incorrectos cuando se usó para calcular algo más. La variable const se optimizó, su antiguo valor se convirtió en una constante en tiempo de compilación. Verdaderamente "comportamiento inesperado".Está bien, tal vez no;)

Ejemplo:

const int x = 2; 
const_cast<int&>(x) = 3; 
int y = x * 2; 
cout << y << endl; 
+0

Nada de esto es inesperado. 'const_cast' es solo para descartar' const'ness de referencias o punteros, a objetos referidos que no fueron declarados 'const' (por ejemplo, cuando se trata de una API incorrecta o tomar un atajo que define un par de llamadas' operator'). En contraste, el Estándar muy deliberadamente declara que descartar el 'const'ness de una variable que originalmente fue declarada' const' es un comportamiento indefinido. –