¿Qué tipo de optimización ofrece la tecnología en C / C ++? (Si alguna)

Sé que, siempre que sea posible, debe usar la palabra clave const al pasar parámetros por referencia o por puntero por razones de legibilidad. ¿Hay alguna optimización que el comstackdor pueda hacer si especifico que un argumento es constante?

Puede haber algunos casos:

Parámetros de función:

Referencia constante:

void foo(const SomeClass& obj) 

Objeto Constant SomeClass:

 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 de comstackdor ofrece cada una (si las hay)?

[Los lectores deben tener en cuenta que la mayoría de esta publicación se ha eliminado de un artículo de Herb Sutter – http://www.gotw.ca/gotw/081.htm – sin atribución del OP.]

CASO 1:-

Cuando declaras un const en tu progtwig,

 int const x = 2; 

El comstackdor puede optimizar esta función al no proporcionar almacenamiento a esta variable, sino agregarla en la tabla de símbolos. Por lo tanto, la lectura posterior solo necesita indirección en la tabla de símbolos en lugar de instrucciones para recuperar el valor de la memoria.

NOTA: – Si haces algo como a continuación: –

 const int x = 1; const int* y = &x; 

Entonces esto obligaría al comstackdor 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 yo sé, no hay una ganancia de rendimiento sustancial por usar const sino que es un medio para garantizar la corrección.

CASE_2: –

“¿Declarar el parámetro y / o el valor de retorno como const ayuda al comstackdor a generar más código ó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 comstackdor?

=> ¿Podría evitar una copia del parámetro o el valor de retorno?

No, ya que el argumento ya se aprobó por referencia.

=> ¿Podría poner una copia de x o de algo en la memoria de solo lectura?

No, ya que tanto x como algunosY viven fuera de su scope y provienen y / o son dados al mundo exterior. Incluso si se asigna dinámicamente algoY sobre la marcha dentro de f (), se le entrega a la persona que llama.

Ques => ¿Qué pasa con las posibles optimizaciones de código que aparece dentro del cuerpo de f ()? Debido a la const, ¿podría el comstackdor mejorar de algún modo el código que genera para el cuerpo de f ()?

Incluso cuando se llama a una función de miembro constante, el comstackdor no puede suponer que los bits del objeto x u objeto no se modificarán. Además, hay problemas adicionales (a menos que el comstackdor realice la optimización global): el comstackdor también puede no estar seguro de que ningún otro código pueda tener una referencia no constante que alise el mismo objeto como x y / o Y, y si alguno de ellos las referencias no const al mismo objeto podrían usarse de manera incidental durante la ejecución de f (); y el comstackdor puede que ni siquiera sepa si los objetos reales, a los que xy algunos SON simplemente referencias, en realidad se declararon const en primer lugar.

CASE_3: –

  void f( const Z z ) { // ... } 

Ques => ¿Habrá alguna optimización en esto?

Sí, porque el comstackdor sabe que z realmente es un objeto const, 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 comstackdor puede estar seguro de que las partes no mutables de z no cambian durante la llamada a g ()

Antes de dar cualquier respuesta, quiero enfatizar que la razón para usar o no const debe ser realmente para la corrección del progtwig y para la claridad de otros desarrolladores más que para las optimizaciones del comstackdor; es decir, hacer que un parámetro const documente que el método no modificará ese parámetro, y hacer que una función miembro const documentos que ese miembro no modificará el objeto del cual es miembro (al menos no de una manera que logicamente cambie la salida) de cualquier otra función miembro de 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 se leen y se hacen no mutar el objeto en cuestión).

En términos de optimizaciones que un comstackdor podría hacer, al menos en teoría, aunque en un modo de optimización que le permita hacer 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); } 

Supongamos que la función de length se marca como const pero en realidad es una operación costosa (digamos que en realidad opera en tiempo O (n) en lugar de O (1) tiempo). Si la función f toma su parámetro por const reference, entonces el comstackdor 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 por una referencia mutable, entonces la length tendría que ser recalculada en cada iteración del bucle, ya que f podría haber modificado el objeto de una manera para producir un cambio en el valor.

Como se señala en los comentarios, esto está asumiendo una serie de advertencias adicionales y solo sería posible cuando se invoca el comstackdor en un modo no estándar que le permite 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 a una referencia mutable).

Parámetros de función:

const no es significativo para la memoria a la que se hace referencia. Es como atar una mano detrás de la espalda del optimizador.

Supongamos que llama a otra función (por ejemplo, la void bar() ) en foo que no tiene definición visible. El optimizador tendrá una restricción porque no tiene forma de saber si la 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 aliasing introduce restricciones significativas para los optimizadores en esta área.

Aunque no lo haya preguntado, los valores de const para los parámetros de 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 de 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 de const . No está definido modificar un objeto const o una constante conocida, por lo que el comstackdor puede realizar algunas optimizaciones; asume que no invocas 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 a la que se hace referencia se puede modificar 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 devolución de la función.

De nuevo, un valor u objeto const es diferente:

extern const char foo[];

SomeClass* const pObj crea un objeto constante de tipo puntero. No existe un método seguro para cambiar dicho objeto, por lo que el comstackdor 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 del tipo afectará a la resolución de sobrecarga y posiblemente resultará en la selección de funciones diferentes y más rápidas.

Los efectos exactos de const difieren para cada contexto donde se usa. Si const se usa al declarar una variable, es físicamente constante y reside de forma potente en la memoria de solo lectura.

 const int x = 123; 

Tratar de expulsar la consistencia es un comportamiento indefinido:

Aunque const_cast puede eliminar la constness o volatilidad de cualquier puntero o referencia, usar el puntero o referencia resultante para escribir en un objeto que fue declarado const o para acceder a un objeto que fue declarado volátil invoca un comportamiento indefinido. cppreference / const_cast

Entonces, en este caso, el comstackdor puede suponer que el valor de x siempre es 123 . Esto abre un potencial de optimización (propagación de constantes)

Para funciones, es una cuestión diferente. Suponer:

 void doFancyStuff(const MyObject& o); 

nuestra función doFancyStuff puede hacer cualquiera de las siguientes cosas con o .

  1. no modificar el objeto.
  2. descartar la constness, luego modificar el objeto
  3. 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 const, invocará un comportamiento indefinido con # 2.

Pregunta del Gurú: ¿los siguientes invocarán un comportamiento indefinido?

 const int x = 1; auto lam = [x]() mutable {const_cast(x) = 2;}; lam();