parametros para librerias con compilar compilador comandos comando archivo c gcc attributes lazy-initialization

para - librerias gcc



Atributos de gcc para funciones de inicio en primer uso (1)

Digo que esto es correcto en base a mi comprensión de pure y const , pero si alguien tiene una definición precisa de los dos, por favor hable. Esto se vuelve complicado porque la documentación de GCC no establece exactamente lo que significa para una función tener "ningún efecto excepto el valor de retorno" ( puro ) o "no examinar ningún valor excepto sus argumentos" (para const ). Obviamente, todas las funciones tienen algunos efectos (usan ciclos de procesador, modifican la memoria) y examinan algunos valores (el código de función, constantes).

Los "efectos secundarios" tendrían que definirse en términos de la semántica del lenguaje de programación C, pero podemos adivinar qué quiere decir la gente del CCG en función del propósito de estos atributos, que es habilitar optimizaciones adicionales (al menos, eso es lo que asumir que son para).

Perdóname si algunos de los siguientes son demasiado básicos ...

Las funciones puras pueden participar en la eliminación común de subexpresiones. Su característica es que no modifican el entorno, por lo que el compilador puede llamarlo menos veces sin cambiar la semántica del programa.

z = f(x); y = f(x);

se convierte en:

z = y = f(x);

O se elimina por completo si z y y no se utilizan.

Así que mi mejor estimación es que una definición de trabajo de "puro" es "cualquier función que pueda llamarse menos veces sin cambiar la semántica del programa". Sin embargo, las llamadas a funciones no se pueden mover, por ejemplo,

size_t l = strlen(str); // strlen is pure *some_ptr = ''/0''; // Obviously, strlen can''t be moved here...

Las funciones de Const se pueden reordenar porque no dependen del entorno dinámico.

// Assuming x and y not aliased, sin can be moved anywhere *some_ptr = ''/0''; double y = sin(x); *other_ptr = ''/0'';

Así que mi mejor suposición es que una definición de trabajo de "const" es "cualquier función que se pueda llamar en cualquier punto sin cambiar la semántica del programa". Sin embargo, existe un peligro:

__attribute__((const)) double big_math_func(double x, double theta, double iota) { static double table[512]; static bool initted = false; if (!initted) { ... initted = true; } ... return result; }

Como es const, el compilador podría reordenarlo ...

pthread_mutex_lock(&mutex); ... z = big_math_func(x, theta, iota); ... pthread_mutex_unlock(&mutex); // big_math_func might go here, if the compiler wants to

En este caso, podría llamarse simultáneamente desde dos procesadores a pesar de que solo aparece dentro de una sección crítica en su código. Entonces, el procesador podría decidir posponer los cambios en la table después de que un cambio al que ya se initted haya initted , lo cual es una mala noticia. Puede resolver esto con barreras de memoria o pthread_once .

No creo que este error aparezca nunca en x86, y no creo que aparezca en muchos sistemas que no tienen procesadores físicos múltiples (no núcleos). Por lo tanto, funcionará bien durante años y luego fallará de repente en una computadora POWER de doble socket.

Conclusión: la ventaja de estas definiciones es que dejan en claro qué tipo de cambios puede hacer el compilador en presencia de estos atributos, que (creo) es algo vago en los documentos de GCC. La desventaja es que no está claro que estas sean las definiciones utilizadas por el equipo de GCC.

Si mira la especificación del lenguaje Haskell, por ejemplo, encontrará una definición mucho más precisa de pureza, ya que la pureza es tan importante para el lenguaje Haskell.

Editar: No he podido obligar a GCC ni a Clang a mover una llamada de función __attribute__((const)) solitaria a través de otra llamada de función, pero parece totalmente posible que en el futuro, algo así ocurra. ¿Recuerda cuando -fstrict-aliasing convirtió en el predeterminado, y todo el mundo de repente tenía muchos más errores en sus programas? Son cosas así que me vuelven cauteloso.

Me parece que cuando marca una función __attribute__((const)) , le está prometiendo al compilador que el resultado de la llamada a la función es el mismo sin importar cuándo se llama durante la ejecución del programa, siempre que los parámetros sean los mismo.

Sin embargo, se me ocurrió una forma de mover una función const fuera de una sección crítica, aunque la forma en que lo hice podría llamarse "trampa" de algún tipo.

__attribute__((const)) extern int const_func(int x); int func(int x) { int y1, y2; y1 = const_func(x); pthread_mutex_lock(&mutex); y2 = const_func(x); pthread_mutex_unlock(&mutex); return y1 + y2; }

El compilador traduce esto en el siguiente código (del ensamblado):

int func(int x) { int y; y = const_func(x); pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); return y * 2; }

Tenga en cuenta que esto no ocurrirá solo con __attribute__((pure)) , el atributo const y solo el atributo const activará este comportamiento.

Como puede ver, la llamada dentro de la sección crítica desapareció. Parece bastante arbitrario que se mantuviera la llamada anterior, y no estaría dispuesto a apostar que el compilador no tomará, en una futura versión, una decisión diferente sobre qué llamada mantener o si podría mover la función a alguna parte. completamente por completo

Conclusión 2: pise con cuidado, porque si no sabe qué promesas está haciendo al compilador, una versión futura del compilador podría sorprenderlo.

He estado usando los atributos gcc const y pure para funciones que devuelven un puntero a datos "constantes" que se asignan e inicializan en el primer uso, es decir, donde la función devolverá el mismo valor cada vez que se llame. Como ejemplo (no es mi caso de uso, sino un ejemplo bien conocido) pienso en una función que asigna y calcula tablas de búsqueda trigonométricas en la primera llamada y simplemente devuelve un puntero a las tablas existentes después de la primera llamada.

El problema: me han dicho que este uso es incorrecto porque estos atributos prohíben los efectos secundarios, y que el compilador podría incluso optimizar la llamada completamente en algunos casos si no se utiliza el valor de retorno. ¿Es seguro mi uso de const / pure attributes, o hay otra forma de decirle al compilador que N>1 llama a la función es equivalente a 1 llamada a la función, pero que 1 llamada a la función no es equivalente a 0 llamadas? a la función? ¿O en otras palabras, que la función solo tiene efectos secundarios la primera vez que se llama?