¿Qué tipo de optimización ofrece const en C/C++?
Sé que, siempre que sea posible, debes usar la palabra clave const al pasar parámetros por referencia o por puntero por razones de legibilidad. ¿Hay alguna optimización que pueda hacer el compilador si especifico que un argumento es constante?
Podría haber algunos casos:
Parámetros de función:
Referencia constante:
void foo(const SomeClass& obj)
Objeto SomeClass constante:
void foo(const SomeClass* pObj)
Y puntero constante a SomeClass:
void foo(SomeClass* const pObj)
Declaraciones de variables:
const int i = 1234
Declaraciones de funciones:
const char* foo()
¿Qué tipo de optimizaciones del compilador ofrece cada uno (si corresponde)?
La siguiente es una paráfrasis de este artículo .
Caso 1:
Cuando declaras a const
en tu programa,
int const x = 2;
El compilador puede optimizar esto const
al no proporcionar almacenamiento para esta variable; en su lugar, se puede agregar a la tabla de símbolos. Por lo tanto, una lectura posterior solo necesita una dirección indirecta a la tabla de símbolos en lugar de instrucciones para recuperar el valor de la memoria.
Nota: Si haces algo como:
const int x = 1;
const int* y = &x;
Entonces esto obligaría al compilador a asignar espacio para x
. Por tanto, ese grado de optimización no es posible para este caso.
En términos de parámetros de función, const
significa que el parámetro no se modifica en la función. Hasta donde yo sé, no hay una mejora sustancial en el rendimiento al usar const
; más bien es un medio para garantizar la corrección.
Caso 2:
"¿Declarar el parámetro y/o el valor de retorno como constante ayuda al compilador a generar un código más óptimo?"
const Y& f( const X& x )
{
// ... do something with x and find a Y object ...
return someY;
}
¿Qué podría hacer mejor el compilador? ¿Podría evitar una copia del parámetro o del valor de retorno?
No, ya que el argumento ya se pasa por referencia.
¿Podría poner una copia de x o algo Y en la memoria de sólo lectura?
No, ya que ambos x
viven someY
fuera de su ámbito y provienen y/o son dados al mundo exterior. Incluso si someY
se asigna dinámicamente sobre la marcha dentro de f()
sí mismo, él y su propiedad se entregan a la persona que llama.
¿Qué pasa con las posibles optimizaciones del código que aparece dentro del cuerpo de f()? Debido a la constante, ¿podría el compilador mejorar de alguna manera el código que genera para el cuerpo de f()?
Incluso cuando llama a una función miembro constante, el compilador no puede asumir que los bits del objeto x
o del objeto someY
no se cambiarán. Además, existen problemas adicionales (a menos que el compilador realice una optimización global): es posible que el compilador tampoco esté seguro de que ningún otro código pueda tener una referencia no constante que alias el mismo objeto como x
y/o someY
, y si alguna de dichas referencias no constantes. Las referencias const al mismo objeto pueden usarse incidentalmente durante la ejecución de f();
y es posible que el compilador ni siquiera sepa si los objetos reales, a los cuales x
y someY
son meras referencias, realmente fueron declarados const en primer lugar.
Caso 3:
void f( const Z z )
{
// ...
}
¿Habrá alguna optimización en esto?
Sí, porque el compilador sabe que z
realmente es un objeto constante, podría realizar algunas optimizaciones útiles incluso sin un análisis global. Por ejemplo, si el cuerpo de f()
contiene una llamada como g( &z )
, el compilador puede estar seguro de que las partes no mutables de z
no cambian durante la llamada a g()
.
Parámetros de función:
const
no es significativo para la memoria referenciada. Es como atar una mano a la espalda del optimizador.
Supongamos que llama a otra función (por ejemplo void bar()
) en foo
la que no tiene una definición visible. El optimizador tendrá una restricción porque no tiene forma de saber si bar
ha modificado o no el parámetro de función pasado foo
(por ejemplo, mediante el acceso a la memoria global). La posibilidad de modificar la memoria externamente y los alias introducen restricciones significativas para los optimizadores en esta área.
Aunque no preguntó, const
los valores de los parámetros de la función permiten optimizaciones porque el optimizador tiene garantizado un const
objeto. Por supuesto, el costo de copiar ese parámetro puede ser mucho mayor que los beneficios del optimizador.
Ver: http://www.gotw.ca/gotw/081.htm
Declaraciones de variables:
const int i = 1234
Esto depende de dónde se declara, cuándo se crea y del tipo. Esta categoría es en gran medida donde const
existen optimizaciones. No está definido modificar un const
objeto o una constante conocida, por lo que el compilador puede realizar algunas optimizaciones; se supone que no invoca un comportamiento indefinido y eso introduce algunas garantías.
const int A(10);
foo(A);
// compiler can assume A's not been modified by foo
Obviamente, un optimizador también puede identificar variables que no cambian:
for (int i(0), n(10); i < n; ++i) { // << n is not const
std::cout << i << ' ';
}
Declaraciones de funciones:
const char* foo()
Insignificante. La memoria referenciada podrá ser modificada externamente. Si la variable referenciada devuelta por foo
es visible, entonces un optimizador podría realizar una optimización, pero eso no tiene nada que ver con la presencia/ausencia de const
en el tipo de retorno de la función.
Nuevamente, un const
valor u objeto es diferente:
extern const char foo[];