c++ - programacion - tips para programar en c
¿Qué tipo de optimización ofrece const en C/C++? (5)
Parámetros de la función:
const
no es significativo para la memoria referenciada.
Es como atar una mano a la espalda del optimizador.
Suponga que llama a otra función (por ejemplo,
void bar()
) en
foo
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 a
foo
(por ejemplo, a través del acceso a la memoria global).
El potencial para modificar la memoria externamente y el alias introducen restricciones significativas para los optimizadores en esta área.
Aunque no preguntó, los
valores
const
para los parámetros de la función permiten optimizaciones porque el optimizador tiene garantizado un objeto
const
.
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 variables:
const int i = 1234
Esto depende de dónde se declara, cuándo se crea y el tipo.
Esta categoría es en gran parte donde existen optimizaciones
const
.
No está definido para modificar un objeto
const
o una constante conocida, por lo que el compilador puede realizar algunas optimizaciones;
asume que no invocas comportamientos indefinidos 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 puede modificarse externamente.
Si la variable referenciada devuelta por
foo
es visible, entonces un optimizador podría hacer 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.
De nuevo, un valor u objeto
const
es diferente:
extern const char foo[];
Sé que, siempre que sea posible, debe usar la palabra clave const cuando pase parámetros por referencia o por puntero por razones de legibilidad. ¿Hay alguna optimización que el compilador pueda hacer si especifico que un argumento es constante?
Podría haber algunos casos:
Parámetros de la función:
Referencia constante:
void foo(const SomeClass& obj)
Objeto constante SomeClass:
void foo(const SomeClass* pObj)
Y puntero constante a SomeClass:
void foo(SomeClass* const pObj)
Declaraciones variables:
const int i = 1234
Declaraciones de funciones:
const char* foo()
¿Qué tipo de optimizaciones del compilador ofrece cada una (si hay alguna)?
Antes de dar una respuesta, quiero enfatizar que la razón para usar o no usar
const
realmente debería ser la corrección del programa y la claridad para otros desarrolladores más que para las optimizaciones del compilador;
es decir, hacer un parámetro
const
documenta que el método no modificará ese parámetro, y hacer que una función miembro
const
documente que ese miembro no modificará el objeto del cual es miembro (al menos no de una manera que cambie lógicamente la salida desde cualquier otra función miembro const).
Hacer esto, por ejemplo, permite a los desarrolladores evitar hacer copias innecesarias de objetos (porque no tienen que preocuparse de que el original sea destruido o modificado) o evitar la sincronización innecesaria de hilos (por ejemplo, sabiendo que todos los hilos simplemente leen y hacen no mutar el objeto en cuestión).
En términos de optimizaciones que un compilador podría hacer, al menos en teoría, aunque en un modo de optimización que le permita hacer ciertas suposiciones no estándar que podrían romper el código estándar de C ++, considere:
for (int i = 0; i < obj.length(); ++i) {
f(obj);
}
Suponga que la función de
length
está marcada como
const
pero en realidad es una operación costosa (digamos que realmente funciona en tiempo O (n) en lugar de tiempo O (1)).
Si la función
f
toma su parámetro por referencia
const
, entonces el compilador podría optimizar este bucle para:
int cached_length = obj.length();
for (int i = 0; i < cached_length; ++i) {
f(obj);
}
... porque el hecho de que la función
f
no modifique el parámetro garantiza que la función de
length
debería devolver los mismos valores cada vez dado que el objeto no ha cambiado.
Sin embargo, si se declara que
f
toma el parámetro mediante una referencia mutable, entonces la
length
necesitaría ser recalculada en cada iteración del ciclo, ya que
f
podría haber modificado el objeto de una manera para producir un cambio en el valor.
Como se señaló en los comentarios, esto supone una serie de advertencias adicionales y solo sería posible al invocar al compilador en un modo no estándar que le permita hacer suposiciones adicionales (como que los métodos
const
son estrictamente una función de sus entradas y que las optimizaciones pueden suponer que el código nunca usará
const_cast
para convertir un parámetro de referencia const en una referencia mutable).
Los efectos exactos de const difieren para cada contexto donde se usa. Si se usa const mientras se declara una variable, es físicamente constante y reside de manera potente en la memoria de solo lectura.
const int x = 123;
Intentar deshacerse de la constancia es un comportamiento indefinido:
Aunque const_cast puede eliminar la constidad o la volatilidad de cualquier puntero o referencia, usar el puntero o referencia resultante para escribir en un objeto que se declaró const o para acceder a un objeto que se declaró volátil invoca un comportamiento indefinido. cppreference/const_cast
Entonces, en este caso, el compilador puede suponer que el valor de
x
siempre es
123
.
Esto abre un potencial de optimización (propagación constante)
Para las funciones es un asunto diferente. Suponer:
void doFancyStuff(const MyObject& o);
nuestra función
doFancyStuff
puede hacer cualquiera de las siguientes cosas con
o
.
- No modificar el objeto.
- desechar la constidad, luego modificar el objeto
-
modificar un miembro de datos
mutable
de MyObject
Tenga en cuenta que si llama a nuestra función con una instancia de MyObject que se declaró como constante, invocará un comportamiento indefinido con # 2.
Pregunta del gurú: ¿invocará el siguiente comportamiento indefinido?
const int x = 1;
auto lam = [x]() mutable {const_cast<int&>(x) = 2;};
lam();
[Los lectores deben tener en cuenta que la mayoría de esta publicación se ha extraído de un artículo de Herb Sutter - http://www.gotw.ca/gotw/081.htm - sin atribución del OP.]
CASO 1:-
Cuando declaras una constante en tu programa,
int const x = 2;
El compilador puede optimizar esta constante al no proporcionar almacenamiento a esta variable, sino agregarla en la tabla de símbolos. Por lo tanto, la lectura posterior solo necesita una dirección indirecta en la tabla de símbolos en lugar de instrucciones para recuperar el valor de la memoria.
NOTA: - Si haces algo como lo siguiente: -
const int x = 1;
const int* y = &x;
Entonces esto obligaría al compilador a asignar espacio para
''x''
.
Entonces, 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 sé, no hay una ganancia sustancial de rendimiento por usar
const
sino que 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;
}
Ques => ¿Qué podría hacer mejor el compilador?
=> ¿Podría evitar una copia del parámetro o el valor de retorno?
No, ya que el argumento ya se pasa por referencia.
=> ¿Podría poner una copia de x o someY en la memoria de solo lectura?
No, ya que tanto x como someY viven fuera de su alcance y provienen y / o se entregan al mundo exterior. Incluso si algoY se asigna dinámicamente sobre la marcha dentro de f () mismo, él y su propiedad se entregan a la persona que llama.
Ques => ¿Qué pasa con las posibles optimizaciones de código que aparecen 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 const, el compilador no puede suponer que los bits del objeto x o del objeto someY no se cambiarán. Además, hay problemas adicionales (a menos que el compilador realice una optimización global): el compilador también puede no estar seguro de que ningún otro código pueda tener una referencia no constante que alias el mismo objeto que x y / o someY, y si alguno las referencias no constantes al mismo objeto pueden usarse de manera incidental durante la ejecución de f (); y el compilador puede que ni siquiera sepa si los objetos reales, a los que x y someY son meras referencias, se declararon realmente const en primer lugar.
CASO_3: -
void f( const Z z )
{
// ...
}
Ques => ¿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 ()
SomeClass* const pObj
crea un objeto constante de tipo puntero.
No existe un método seguro para cambiar dicho objeto, por lo que el compilador puede, por ejemplo, almacenarlo en caché en un registro con solo una lectura de memoria, incluso si se toma su dirección.
Los otros no habilitan ninguna optimización específicamente, aunque el calificador
const
en el tipo afectará la resolución de sobrecarga y posiblemente resultará en la selección de funciones diferentes y más rápidas.