2012-02-04 18 views
5

Ayer, tuve una entrevista. ¿Allí me preguntaron cuándo ocurre la optimización del código? Say,¿Cuándo ocurre la optimización del código?

int abc;//Global variable 
abc = 3; 
if(abc == 3) 
{ 
    printf("abc will be always 3"); 
} 
else 
{ 
    printf("This will never executed"); 
} 

Ahora la pregunta es cuando ocurre la optimización? A ... En tiempo de ejecución B ... En tiempo de compilación. Respondí en tiempo de compilación ... Para mí, pensé, el compilador comprueba la palabra clave volátil en tiempo de compilación. Si la variable no se declara como volátil, optimiza el código. Pero, cuando el compilador llega a saber eso, ¿esta variable nunca va a ser más que 3? Si está en tiempo de ejecución, ¿cuándo el compilador llega a saber que la variable nunca será diferente de 3? Porque si la variable va a cambiar después de esta parte del código ejecutado. Por favor borre mi duda

Respuesta

4

mayoría optimización de código no puede suceder en tiempo de ejecución, por lo menos no la forma en que quiere decir. El código no puede cambiar durante la ejecución para reflejar un conjunto nuevo o diferente de variables, que simplemente causaría un desorden absoluto.

Lo que se puede hacer en el tiempo de ejecución es elegir la mejor ruta a través del código, pero eso tiene que hacerse principalmente de forma manual para crear rutas separadas, salidas anticipadas, sucursales, etc., para permitir la optimización.

Este código es, como está escrito, permite la optimización en tiempo de compilación porque el compilador puede verificar cualquier posible valor alternativo de abc y, si no encuentra ninguno, optimizar la llamada. El alcance de la búsqueda influye en gran medida si eso puede suceder, sin embargo, y la configuración del compilador influye en eso.

Si el compilador simplemente está optimizando inocentemente los archivos de un solo objeto y su segunda línea está en otro archivo de la sección de impresión, entonces no puede garantizar que abc no cambie, y por lo tanto no podrá optimizar esto en absoluto. Independientemente del uso variable, depende de cuán agresivas sean las configuraciones del compilador y si se les permite descartar ramas muertas, o considerarán hacerlo.La optimización del tamaño puede ser más probable que elimine la rama que la velocidad, pero la configuración media/alta probablemente lo haga de cualquier manera (si es posible).

La mayoría de los compiladores modernos tienen una opción de optimización del programa completo, que retrasa gran parte de la optimización hasta la etapa del enlazador. Esto le permite buscar en todo el programa, descubrir que abc no se cambia ni se usa en ningún otro lugar, y elimina la variable y la falla de la condición. La optimización de todo el programa puede ser mucho más efectiva que la optimización por separado para cada objeto, ya que puede permitir una búsqueda más precisa.

En caso de que el compilador no pueda recortar el código muerto, puede indicarlo (construcciones recientes como constexpr pueden ayudar con eso) o agregar optimizaciones usted mismo. Podría ser tan simple como poner el camino más probable primero e incluir un retorno antes que el otro, evitando que la CPU haga un salto. Ese tipo de micro-optimización es poco probable que sea necesario, ciertamente no en un ejemplo simple como este, donde el if es una gran optimización por sí mismo.

8

El código C generalmente se compila usando una compilación estática (también conocida como por adelantado). El código está escrito en piedra una vez que sale del compilador; no puede cambiar en tiempo de ejecución.

Esto está en contraste con los lenguajes que usan just-in-time compilation, como Java. Allí, las optimizaciones pueden suceder prácticamente en cualquier momento mientras el programa se está ejecutando.

+1

Lo más parecido a la optimización del tiempo de ejecución en C es la optimización basada en el perfil. Usted compila el programa, luego lo ejecuta, mientras utiliza un generador de perfiles para recopilar estadísticas. Luego compila de nuevo, dando estas estadísticas al compilador. La segunda compilación usaría esta información para generar código más rápido. – ugoren

1

No estoy demasiado familiarizado con la forma en que un compilador optimiza el código, sin embargo, sé que desde el código que tiene allí, el compilador puede deducir que abc nunca se cambia. Esto significa que podría sacar la declaración if por completo y simplemente llamar a la primera función printf.

Luego, en el tiempo de ejecución, no queda mucha optimización, ya que el código es bastante sencillo. Si abc cambiaran, obviamente el if no se pudo eludir, sin embargo, en el tiempo de ejecución, hay cosas como la predicción de bifurcación en la CPU que intentará predecir la ruta del código que se está tomando. Lo cual también podría considerarse una forma de optimización.

Nota: No afirmo que esto sea lo que sucede, pero pude ver que un compilador podría optimizar de esa manera, en una configuración de optimización agresiva.

2

en tiempo de compilación :)

(Bueno, en principio, que depende del compilador, etc.)

1

Me pregunto si estaban buscando una respuesta o unas pocas respuestas posibles.

El compilador no hace nada en tiempo de ejecución, por lo general. No es necesario que el binario del compilador y sus componentes estén presentes (para el lenguaje C) en el entorno de tiempo de ejecución.

El tipo de optimización que eliminaría esta rama muerta utilizaría ciertas técnicas para escribir compiladores de optimización. Ver el libro de Muchnik sobre compiladores. Las técnicas utilizadas pueden implican la creación de un gráfico dirigido de bloques básicos, y luego usando uno de:

  • def-uso análisis
  • sola asignación (ssa) análisis estático
    junto con
  • propagación constante (la transformada si en si (3 == 3)
    y luego
  • eliminación expresión constante
    entonces eliminación
  • código muerto/rama muerta

Por otro lado, es posible que algunos compiladores no calculen lo suficiente para eliminar esto durante la optimización.

En ese caso, si ejecutó el código en un chip con predicción de bifurcación, el chip "aprenderá" que la primera bifurcación está pronosticada, y la memoria caché (buscará) esa bifurcación favorablemente. Pero este no es un mecanismo de compilación, generalmente no se llama optimización.

1

Respuesta más simple: La optimización de código ocurre en el momento de la escritura.

Respuesta simple: tal vez, es dependiente del compilador dado el alcance.

Respuesta difícil: Dado el alcance global, el compilador debería dejar eso solo con la suposición de que podría accederse a otra parte del archivo. Pases múltiples podrían optimizarlo. Si el compilador no lo considera estático para el archivo (piense en sistemas con modelos de memoria plana), entonces global es verdaderamente global y la suposición debería ser que cualquier cosa podría cambiar el valor. Esta es la razón por la que evita los globales a menos que la intención sea realmente el acceso global.

Dependiendo del procesador, se aplicaría el argumento de predicción de bifurcación, pero sobre todo es tiempo de compilación o no es en absoluto.

ps: Realmente no me gustan las preguntas de entrevistas como esta.

+0

De acuerdo con su respuesta, pero surgió una duda más, si la variable global (no volátil) se puede cambiar en cualquier momento, entonces ¿por qué necesitamos volatilidad? –

+0

volátil es necesario para un contexto que parece estático. Piense en un valor pasado por puntero. Además, si dirige un mapa de un registro de hardware (piense en el mapa de la memoria periférica o el registro del dispositivo), el valor puede cambiar sin que se ejecute ningún código. – Dtyree

3

No responde la pregunta pero proporciona un ejemplo de optimización de tiempo de compilación. gcc optimiza el código cuando se le pide que lo haga. La opción -O (Optimizar) permite la optimización en diferentes niveles. Se puede usar como -O1, -O2 y -O3. La página del manual de gcc describe con precisión el significado de cada nivel.

La opción -S traduce C en ensamblaje y guarda en archivo .s.

test.c

#include <stdio.h> 

int abc;//Global variable 

void main() 
{ 
    abc = 3; 
    if(abc == 3) 
     printf("abc will be always 3"); 
    else 
     printf("This will never executed"); 
} 

Whitout optimización de gcc las dos cadenas aparecen en el código de montaje.

$ gcc -S test.c; gato test.s

.file "test.c" 
    .comm abc,4,4 
    .section .rodata 
.LC0: 
    .string "abc will be always 3" 
.LC1: 
    .string "This will never executed" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    movl $3, abc(%rip) 
    movl abc(%rip), %eax 
    cmpl $3, %eax 
    jne .L2 
    movl $.LC0, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
    jmp .L1 
.L2: 
    movl $.LC1, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
.L1: 
    popq %rbp 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 

nivel gcc Whit 1 optimización de una sola cadena se traduce en el conjunto de

$ gcc -S -O1 prueba. c; cat test.s

.file "test.c" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "abc will be always 3" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB11: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, abc(%rip) 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE11: 
    .size main, .-main 
    .comm abc,4,4 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 
+0

Excelente .... explicación –

Cuestiones relacionadas