para - Constantes y optimización del compilador en C++
libro de c++ completo (12)
He leído todos los consejos sobre const-correctness en C ++ y que es importante (en parte) porque ayuda al compilador a optimizar tu código. Lo que nunca he visto es una buena explicación sobre cómo el compilador usa esta información para optimizar el código, ni siquiera los buenos libros continúan explicando lo que sucede detrás de las cortinas.
Por ejemplo, cómo el compilador optimiza un método que se declara const frente a uno que no es, pero debería ser. ¿Qué sucede cuando introduces variables mutables? ¿Afectan estas optimizaciones de los métodos const?
Creo que la palabra clave const se introdujo principalmente para la comprobación de compilación de la semántica del programa, no para la optimización.
Herb Sutter, en el artículo de GotW # 81 , explica muy bien por qué el compilador no puede optimizar nada al pasar parámetros por referencia constante, o cuando declara el valor de retorno de const. La razón es que el compilador no tiene forma de asegurarse de que el objeto al que se hace referencia no cambiará, incluso si se declara const: uno podría usar un const_cast, o algún otro código puede tener una referencia no constante en el mismo objeto.
Sin embargo, citando el artículo de Herb Sutter:
Hay [solo] un caso en el que decir "const" puede significar realmente algo, y es cuando los objetos se hacen const en el punto en que se definen. En ese caso, el compilador a menudo puede colocar dichos objetos "realmente const" en memoria de solo lectura [...].
Hay mucho más en este artículo, por lo que te animo a que lo leas: tendrás una mejor comprensión de la optimización constante después de eso.
Despreciemos los métodos y solo miramos los objetos const; el compilador tiene muchas más oportunidades para la optimización aquí. Si un objeto se declara const, entonces (ISO / IEC 14882: 2003 7.1.5.1 (4)):
Excepto que cualquier miembro de la clase declarado mutable (7.1.1) se puede modificar, cualquier intento de modificar un objeto const durante su ciclo de vida (3.8) da como resultado un comportamiento indefinido.
Deja de lado los objetos que pueden tener miembros mutables: el compilador puede suponer que el objeto no se modificará, por lo tanto, puede generar optimizaciones significativas. Estas optimizaciones pueden incluir cosas como:
- incorporando el valor del objeto directamente en los códigos de operación de las máquinas
- eliminación completa del código que nunca se puede alcanzar porque el objeto const se usa en una expresión condicional que se conoce en tiempo de compilación
- loop que se desenrolla si el objeto const controla el número de iteraciones de un bucle
Tenga en cuenta que esto solo se aplica si el objeto real es const: no se aplica a los objetos a los que se accede mediante punteros o referencias const porque esas rutas de acceso pueden conducir a objetos que no son const (incluso está bien definido para cambiar objetos aunque punteros / referencias siempre que el objeto real no sea const y deseche la constness de la ruta de acceso al objeto).
En la práctica, no creo que haya compiladores que realicen optimizaciones significativas para todo tipo de objetos const. pero para los objetos que son tipos primitivos (ints, chars, etc.), creo que los compiladores pueden ser bastante agresivos para optimizar el uso de esos elementos.
El punto más obvio donde const
es una optimización directa es pasar argumentos a una función. A menudo es importante asegurarse de que la función no modifique los datos, por lo que las únicas opciones reales para la firma de la función son estas:
void f(Type dont_modify); // or
void f(Type const& dont_modify);
Por supuesto, la verdadera magia aquí es pasar una referencia en lugar de crear una copia (costosa) del objeto. Pero si la referencia no estuviera marcada como const
, esto debilitaría la semántica de esta función y tendría efectos negativos (como dificultar el seguimiento de errores). Por lo tanto, const
permite una optimización aquí.
/ EDITAR: en realidad, un buen compilador puede analizar el flujo de control de la función, determinar que no modifique el argumento y hacer la optimización (pasando una referencia en lugar de una copia). const
aquí es simplemente una ayuda para el compilador. Sin embargo, dado que C ++ tiene una semántica bastante complicada y dicho análisis de flujo de control puede ser muy costoso para grandes funciones, probablemente no deberíamos confiar en los compiladores para esto. ¿Alguien tiene datos para respaldarme / probar que estoy equivocado?
/ EDIT2: y sí, tan pronto como entran en juego los constructores de copia personalizada, se vuelve aún más complicado porque desafortunadamente los compiladores no pueden omitir llamarlos en esta situación.
Esas son todas respuestas verdaderas, pero las respuestas y la pregunta parecen presumir una cosa: la optimización del compilador realmente importa.
Solo hay un tipo de código en el que importa la optimización del compilador, es decir en el código que es
- un lazo interno apretado,
- en el código que compila, a diferencia de una biblioteca de terceros,
- no contiene llamadas a función o método (incluso las ocultas),
- donde el contador del programa pasa una notable fracción de su tiempo
Si el otro 99% del código se optimiza en el grado N, no hará una gran diferencia, porque solo importa en el código donde el contador del programa realmente gasta tiempo (que puede encontrar por muestreo).
Este código,
class Test
{
public:
Test (int value) : m_value (value)
{
}
void SetValue (int value) const
{
const_cast <Test&>(*this).MySetValue (value);
}
int Value () const
{
return m_value;
}
private:
void MySetValue (int value)
{
m_value = value;
}
int
m_value;
};
void modify (const Test &test, int value)
{
test.SetValue (value);
}
void main ()
{
const Test
test (100);
cout << test.Value () << endl;
modify (test, 50);
cout << test.Value () << endl;
}
productos:
100
50
lo que significa que el objeto const declarado ha sido alterado en una función miembro miembro. La presencia de const_cast (y la palabra clave mutable) en el lenguaje C ++ significa que la palabra clave const no puede ayudar al compilador a generar código optimizado. Y como señalé en mis publicaciones anteriores, incluso puede producir resultados inesperados.
Como regla general:
const! = optimización
De hecho, este es un modificador legal de C ++:
volatile const
La razón principal para tener métodos como const es la const corrección, no para la posible optimización de la compilación del método en sí.
Si las variables son const, pueden (en teoría) optimizarse. Pero solo el alcance puede ser visto por el compilador. Después de todo, el compilador debe permitir que se modifiquen con un const_cast en otro lugar.
Me sorprendería si el optimizador realmente pone mucha acción en una declaración constante. Hay un montón de código que terminará alejando la const-ness, sería un optimizador muy imprudente que se basó en la declaración del programador para asumir cuando el estado puede cambiar.
Meh. Const-correctness es más una cosa de estilo / comprobación de errores que una optimización. Un compilador de optimización completo seguirá el uso de variables y puede detectar cuándo una variable es const o no efectiva.
Además de eso, el compilador no puede confiar en que le diga la verdad: podría estar descartando el const dentro de una función de biblioteca que desconoce.
Así que sí, la corrección de const es algo digno de apuntar, pero no le dice nada al compilador que no se resolverá por sí mismo, asumiendo un buen compilador de optimización.
No optimiza la función que se declara const.
Puede optimizar las funciones que llaman a la función que se declara const.
void someType::somefunc();
void MyFunc()
{
someType A(4); //
Fling(A.m_val);
A.someFunc();
Flong(A.m_val);
}
Aquí para llamar a Fling, el valud A.m_val tenía que cargarse en un registro de CPU. Si algunaFunc () no es const, el valor debería volver a cargarse antes de llamar a Flong (). Si algunaFunc es const, entonces podemos llamar a Flong con el valor que todavía está en el registro.
const-correctness también es útil como documentación. Si una función o parámetro está listado como const, no necesito preocuparme por el cambio de valor desde mi código (a menos que alguien más en el equipo esté siendo muy malo). Sin embargo, no estoy seguro de que realmente valga la pena si no estuviera integrado en la biblioteca.
const
ayuda a los compiladores a optimizar principalmente porque te hace escribir código optimizable. A menos que arroje const_cast
.
comienza el handwaving
Esencialmente, cuanto antes se arreglen los datos, más podrá el compilador moverse alrededor de la asignación real de los datos, asegurando que la tubería no se estanque.
fin handwaving