c++ - tension - puesta a tierra normas
En C++, ¿puedo declarar una referencia para indicar que nada la modificará? (5)
Si lo hago
typedef void Cb();
int foo(int const& a, Cb cb) {
int x = a;
cb();
return x - a;
}
y compilar con g++ -O3 -save-temps -c foo.cpp
, veo que la resta se conserva, mientras que si cb();
está comentado, toda la función se optimiza para
xorl %eax, %eax
¿Hay algo que pueda hacer a la especificación del parámetro a
para que la resta se optimice independientemente de la llamada a cb()
y sin forzar a que sea una referencia única (es decir, que se pueda consultar en otra parte, pero que a través de ninguna de esas referencias será modificada)?
¿Por qué no mueves el int x = a;
línea debajo de la llamada de función?
Si cb()
no influye ni en x
ni en a
, deberías hacerlo bien, y creo que el compilador optimizará la llamada nuevamente, porque x no puede cambiar entre las dos llamadas. Si hay una razón por la que no puede reordenar estas dos llamadas, probablemente no pueda optimizarlas en primer lugar.
Sin embargo, esto no es algo que pueda sugerir al compilador, ya que no hay forma de garantizar que ni x ni a hayan cambiado después de la llamada a cb()
.
Piense en esto como un orden de acceso de lectura / escritura . Si no tiene acceso de lectura o escritura a a
y x
durante cb()
, puede hacer un reordenamiento manual de la función-llamada.
Si se escribe a o x
, no puede reordenar y la optimización no sería correcta.
Si se lee x
, no puede reordenar la llamada a la función, pero si x
realmente solo se lee, se podría leer en lugar de un, y definir x
solo después de la llamada, ya que tendrá el mismo valor que a
, si lo hubiera declarado antes del llamada.
Existe la extensión __restrict
, puede probar esto en gcc.godbolt.org :
typedef void Cb();
int foo(const int & __restrict a, Cb cb) {
int x = a;
cb();
return x - a;
}
Curiosamente, gcc.godbolt.org , gcc no lo hace .
Observe que el aliasing de tipo restringido se considera parte del estándar de C ++:
Tal vez en el futuro puedas hacerlo por el estándar.
Hacer la optimización sugerida sería incorrecto porque el resto del código podría ser:
static int var;
void func()
{
var++;
}
// ...
foo(var, func);
No tengo conocimiento de ningún atributo específico del compilador que pueda establecer para decir que cb()
no modificará a
.
Podría usar la restrict
C en la referencia, si su compilador soporta esa extensión.
(Algunos compiladores permiten __restrict
o __restrict__
, que forman parte del espacio de nombres de las implementaciones).
Esa es una promesa de usted al compilador de que el objeto no tiene alias en ninguna parte y, por lo tanto, puede optimizarlo.
Si mentiste al compilador, bueno, obtienes el código roto que mereces.
__attribute __ ((const)) es suficiente
Como se documenta en: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html , le dice al compilador que la función dada no modifica los globales (aunque puede leerlos).
También existe el subconjunto pure
de const
, que también prohíbe las lecturas globales.
Considere el siguiente ejemplo simplificado:
int __attribute__((const)) g();
int f(int a) {
int x = a;
g();
return x - a;
}
En g++
4.8 x86_64 -O3
, se compila a:
-
xor %eax,%eax
conconst
- Una función grande que no supone que
a
no se modifique sinconst
Parece que no hay un atributo de grano más fino que diga que una función no modifica una variable dada como usted lo requiere.
¿Se puede aplicar __attribute __ ((const)) a los punteros de función?
Intentemos:
int f(int& a, void __attribute__((const)) (*g)(void)) {
int x = a;
(*g)();
return x - a;
}
Una vez más, se compila en xor %eax,%eax
y en una función grande sin const
, por lo que la respuesta es sí.
Esta sintaxis se solicitó en: Puntero de función a __attribute __ ((const)) function?
También apareció en 2011 en la lista de correo en: http://comments.gmane.org/gmane.comp.gcc.help/38052 Al menos al tiempo, solo funcionaba para algunos atributos.
¿Se puede agregar __attribute __ ((const)) a typedef?
Esto funciona:
typedef void __attribute__((const)) (*g_t)(void);
int f(int& a, g_t g) {
int x = a;
(*g)();
return x - a;
}
o:
typedef void (g_t)(void);
int f(int& a, g_t __attribute__((const)) g) {
int x = a;
g();
return x - a;
}
Pero no pude encontrar una manera de poner el atributo en el typedef y pasar una función, no un puntero, como:
typedef void __attribute__((const)) (g_t)(void);
GCC da una advertencia diciendo que el atributo fue ignorado en este caso.