Para una función que toma una estructura const, ¿el compilador no optimiza el cuerpo de la función?

5 minutos de lectura

Avatar de usuario de Jan Jongboom
Jan Jong Boom

Tengo el siguiente fragmento de código:

#include <stdio.h>

typedef struct {
    bool some_var;
} model_t;

const model_t model = {
    true
};

void bla(const model_t *m) {
    if (m->some_var) {
        printf("Some var is true!\n");
    }
    else {
        printf("Some var is false!\n");
    }
}

int main() {
    bla(&model);
}

Me imagino que el compilador tiene toda la información requerida para eliminar el else cláusula en el bla() función. La única ruta de código que llama a la función proviene de main y toma const model_t, por lo que debería poder darse cuenta de que esa ruta de código no se está utilizando. Sin embargo:

sin en línea

Con GCC 12.2 vemos que la segunda parte está enlazada.

Si yo inline la función esto desaparece sin embargo:

con en línea

¿Que me estoy perdiendo aqui? ¿Y hay alguna forma en que pueda hacer que el compilador haga un trabajo más inteligente? Esto sucede tanto en C como en C++ con -O3 y -Os.

  • El compilador no puede optimizar la ruta else ya que el archivo de objeto podría estar vinculado con cualquier otro código. Esto sería diferente si la función fuera estática.

    – MrTux

    29 ago a las 9:40


  • Si tu haces bla staticel compilador puede optimizarlo.

    – Özgür Murat Sağdıçoğlu

    29 ago a las 9:42

  • El compilador hizo en línea el bla llamar main y optimizado el else bifurcarse allí. Solo tiene que emitir la función en caso de que otra unidad de traducción la use. (El explorador del compilador le muestra el resultado después de la compilación antes de vincular).

    – usuario17732522

    29 ago a las 9:44


  • @Zakk ¿A qué te refieres? Que bla estaba en línea? La imagen en la pregunta muestra la salida del ensamblado del compilador. En lo emitido main función no hay llamada a blasolo una llamada a puts con su argumento cargado incondicionalmente desde .LC0 que es la cuerda en la primera rama de la if. Así que la ramificación se optimizó.

    – usuario17732522

    29 ago a las 13:19

  • Tus imágenes deben ser pernodios.org enlaces completos, no solo a la imagen en imgur! Sin embargo, el código no debe publicarse como imágenes en primer lugar.

    – Peter Cordes

    30 de agosto a las 14:57


Avatar de usuario de Ayxan Haqverdili
Ayxan Haqverdili

el compilador lo hace elimine la ruta else en la función en línea en main. Está confundiendo la función global que no se llama de todos modos y que el enlazador descartará eventualmente.

Si usas el -fprograma completo marca para que el compilador sepa que no se vinculará ningún otro archivo, ese segmento no utilizado se descarta:

[See online]

Ingrese la descripción de la imagen aquí

Además, usas static o inline palabras clave para lograr algo similar.

Avatar de usuario de MrTux
mrtux

El compilador no puede optimizar la ruta else ya que el archivo de objeto podría estar vinculado con cualquier otro código. Esto sería diferente si la función fuera estática o utilizara la optimización de todo el programa.

  • Si la función fue declarada static simplemente no se emitiría (igual que con inline). Todos los sitios de llamadas están integrados de todos modos. La función emitida en la pregunta nunca se usó.

    – usuario17732522

    29 ago a las 9:47


La única ruta de código que llama a la función proviene de main

GCC no puede saber eso a menos que usted lo diga con -fwhole-program o tal vez -flto (optimización del tiempo de enlace). De lo contrario, debe asumir que algún constructor estático en otra unidad de compilación podría llamarlo. (Incluyendo posiblemente en una biblioteca compartida, pero otra .cpp con el que te vinculas podría hacerlo.) por ejemplo

// another .cpp
typedef struct {  bool some_var; } model_t;
void bla(const model_t *m);              // declare the things from the other .cpp

int foo() {
    model_t model = {false};
    bla(&model);
    return 1;
}
int some_global = foo();  // C++ only: non-constant static initializer.

Ejemplo en Godbolt con estas líneas en la misma unidad de compilación que main, mostrando que genera ambos Some var is false! y entonces Some var is true!sin haber cambiado el código de main.

ISO C no tiene formas fáciles de ejecutar el código de inicio, pero GNU C (y GCC específicamente) tienen formas de ejecutar el código al inicio, no llamado por main. Esto funciona incluso para bibliotecas compartidas.


Con -fwhole-programla optimización adecuada sería simplemente no emitir una definición para él en absoluto, ya que ya está integrado en el sitio de llamada en main. como con inline (En C++, una promesa de que cualquier otra persona que llama en otra unidad de compilación puede ver su propia definición de la función) o static (privado para esta unidad de compilación).

En el interior main, ha optimizado la rama después de una propagación constante. Si ejecutó el programa, ninguna rama se ejecutaría realmente; nada llama a la definición independiente de la función.


La definición independiente de la función no sabe que el único valor posible para m es &model. Si pones eso dentro de la función, entonces podría optimizarse como esperas.

Solamente -fPIC obligaría al compilador a considerar la posibilidad de interposición de símbolos, por lo que la definición de const model_t model no es el que está en efecto después de la vinculación (dinámica). Pero está compilando código para un ejecutable, no para una biblioteca. (Puede deshabilitar la interposición de símbolos para una variable global dándole visibilidad “oculta”, __attribute__((visibility("hidden")))o usar -fvisibility=hidden para que sea el predeterminado).

¿Ha sido útil esta solución?