Esto es bastante estándar en la ingeniería de software en su conjunto: cuando optimiza el código, el compilador puede reorganizar las cosas más o menos como lo desee, siempre que no pueda notar ninguna diferencia en la operación. Entonces, por ejemplo, si inicializa una variable dentro de cada iteración de un bucle, y nunca cambia la variable dentro del bucle, el optimizador puede mover esa inicialización fuera del bucle, para que no pierda el tiempo con ella.
También puede darse cuenta de que calcula un número con el que luego no hace nada antes de sobrescribir. En ese caso, podría eliminar la computación inútil.
El problema con la optimización es que querrás poner un punto de interrupción en algún fragmento de código que el optimizador haya movido o eliminado. En ese caso, el depurador no puede hacer lo que desea (generalmente, pondrá el punto de interrupción en algún lugar cercano). Por lo tanto, para hacer que el código generado se parezca más a lo que escribió, desactive las optimizaciones durante la depuración; esto asegura que el código que desea romper está realmente allí.
Sin embargo, debe tener cuidado con esto, ya que dependiendo de su código, ¡la optimización puede romper las cosas! En general, el código que se rompe con un optimizador que funciona correctamente es realmente solo un código defectuoso que se salta con la suya, por lo que generalmente desea averiguar por qué el optimizador lo rompe.
Le envié esta pregunta a Jack Ganssle y esto es lo que me respondió:
fuente
Depende, y esto es generalmente cierto para todas las herramientas, no solo C30.
Las optimizaciones a menudo eliminan y / o reestructuran el código de varias maneras. Su declaración de cambio puede volver a implementarse con una construcción if / else o, en algunos casos, puede eliminarse por completo. y = x * 16 puede ser reemplazado por una serie de desplazamientos a la izquierda, etc. aunque este último tipo de optimización generalmente puede ser atravesado, es principalmente la reestructuración de la declaración de control lo que te da.
Esto puede hacer que sea imposible pasar un depurador a través de su código C porque las estructuras que definió en C ya no existen, fueron reemplazadas o reordenadas por el compilador en algo que el compilador cree que será más rápido o usará menos espacio. También puede hacer que los puntos de interrupción sean imposibles de establecer desde la lista C ya que la instrucción en la que rompió ya no existe. Por ejemplo, puede intentar establecer un punto de interrupción dentro de una instrucción if, pero el compilador puede haber eliminado ese if. Puede intentar establecer un punto de interrupción dentro de un tiempo o para un bucle, pero el compilador decidió desenrollar ese bucle para que ya no exista.
Por esta razón, si puede depurar con optimizaciones desactivadas, generalmente es más fácil. Siempre debe volver a realizar pruebas con optimizaciones activadas. Esta es la única forma en que descubrirás que te perdiste un importante
volatile
y está causando fallas intermitentes (o alguna otra rareza).En el caso del desarrollo integrado, debe tener cuidado con las optimizaciones de todos modos. Específicamente en secciones de código que son críticas en el tiempo, algunas interrupciones, por ejemplo. En estos casos, debe codificar los bits críticos en el ensamblaje o usar las directivas del compilador para asegurarse de que estas secciones no estén optimizadas para que sepa que tienen un tiempo de ejecución fijo o un tiempo de ejecución del peor de los casos fijo.
El otro problema puede ser ajustar el código en el uC, es posible que necesite optimizaciones de densidad de código para simplemente ajustar su código en el chip. Esta es una de las razones por las cuales, por lo general, es una buena idea comenzar con la capacidad de ROM uC más grande de una familia y solo elegir una más pequeña para la fabricación, después de que su código esté bloqueado.
fuente
En general, depuraría con cualquier configuración con la que planeara lanzar. Si iba a lanzar código optimizado, lo depuraría con código optimizado. Si iba a liberar código no optimizado, lo depuraría con código no optimizado. Hago esto por dos razones. Primero, los optimizadores pueden hacer diferencias de tiempo lo suficientemente significativas como para hacer que el producto final se comporte de manera diferente al código no optimizado. En segundo lugar, aunque la mayoría son bastante buenos, los proveedores de compiladores cometen errores y el código optimizado puede producir resultados diferentes del código no optimizado. Como resultado, me gusta obtener el mayor tiempo de prueba posible con cualquier configuración con la que planee lanzar.
Dicho esto, los optimizadores pueden dificultar la depuración como se señaló en las respuestas anteriores. Si encuentro una sección particular de código que es difícil de depurar, apagaré temporalmente el optimizador, realizaré la depuración para que el código funcione, luego volveré a encender el optimizador y probaré una vez más.
fuente
Mi estrategia normal es desarrollar con la optimización final (máximo para el tamaño o la velocidad, según corresponda), pero desactive temporalmente la optimización si necesito depurar o rastrear algo. Esto reduce el riesgo de que surjan errores como resultado del cambio de los niveles de optimización.
Un modo de falla típico es cuando el aumento de la optimización hace que surjan errores no vistos anteriormente debido a que no ha declarado que las variables sean volátiles cuando sea necesario; esto es esencial para decirle al compilador qué cosas no deben 'optimizarse'.
fuente
Use cualquier forma con la que va a lanzar, los depuradores y la compilación para la depuración ocultan muchos (MUCHOS) errores que no ve hasta que compila para el lanzamiento. Para entonces, es mucho más difícil encontrar esos errores, en lugar de depurarlos a medida que avanza. 20 años y algo ahora y nunca tuve un uso para un gdb u otro como depurador, no es necesario mirar variables o un solo paso. Cientos a miles de líneas por día. Por lo tanto, es posible, no se deje llevar a pensar lo contrario.
La compilación para la depuración y luego la compilación para el lanzamiento puede y llevará dos veces más de dos veces el esfuerzo. Si tiene problemas y tiene que usar una herramienta como un depurador, compile para que el depurador resuelva el problema específico y luego vuelva a la operación normal.
Otros problemas también son ciertos, ya que el optimizador hace que el código sea más rápido, por lo que para incrustar en particular sus cambios de tiempo con las opciones del compilador y eso puede afectar la funcionalidad de su programa, aquí nuevamente use la opción de compilación entregable durante toda la fase. Los compiladores también son programas y tienen errores y optimizadores que cometen errores y algunos no tienen fe en eso. Si ese es el caso, no hay nada de malo en compilar sin optimización, simplemente hágalo de esa manera todo el tiempo. La ruta que prefiero es compilar para la optimización, entonces si sospecho que un problema del compilador deshabilita la optimización si eso soluciona, generalmente va y viene a veces examinando la salida del ensamblador para descubrir por qué.
fuente
Siempre desarrollo código con -O0 (opción gcc para desactivar la optimización). Cuando siento que estoy en el punto en el que quiero comenzar a permitir que las cosas se encaminen más hacia un lanzamiento, comenzaré con -Os (optimizar para el tamaño) ya que generalmente cuanto más código pueda mantener en caché, mejor será, incluso si no está súper optimizado.
Me parece que gdb funciona mucho mejor con el código -O0, y es mucho más fácil de seguir si tiene que ingresar al ensamblado. Alternar entre -O0 y -Os también le permite ver lo que el compilador le está haciendo a su código. A veces es una educación bastante interesante, y también puede descubrir errores de compilación ... ¡esas cosas desagradables que te hacen arrancarte el pelo tratando de descubrir qué tiene de malo tu código!
Si realmente lo necesito, comenzaré a agregar en -fdata-secciones y -fcode-secciones con --gc-secciones, lo que permite que el enlazador elimine funciones completas y segmentos de datos que no se utilizan realmente. Hay muchas pequeñas cosas con las que puedes jugar para tratar de reducir las cosas más rápido o hacer las cosas más rápido, pero en general, estos son los únicos trucos que termino usando, y cualquier cosa que tenga que ser más pequeña o más rápida. -montar.
fuente
Sí, deshabilitar las optimizaciones durante la depuración ha sido la mejor práctica durante un tiempo, por tres razones:
Muchas personas van aún más lejos en esta dirección y envían con afirmaciones activadas .
fuente
Simple: las optimizaciones requieren mucho tiempo y pueden ser inútiles si tiene que cambiar ese código más adelante en el desarrollo. Por lo tanto, pueden ser una pérdida de tiempo y dinero.
Sin embargo, son útiles para módulos terminados; partes del código que probablemente ya no necesiten cambios.
fuente
Ciertamente tiene sentido en el caso de los puntos de ruptura ... ya que el compilador puede eliminar muchas declaraciones que en realidad no afectan la memoria.
considera algo como:
podría optimizarse por completo (porque
i
nunca se lee). Desde el punto de vista de su punto de interrupción, parecería que omitió todo ese código, cuando básicamente ni siquiera estaba allí ... Supongo que es por eso que en las funciones de tipo de suspensión a menudo verá algo como:fuente
Si está utilizando el depurador, deshabilitaría las optimizaciones y habilitaría la depuración.
Personalmente, encuentro que el depurador PIC causa más problemas de los que me ayuda a solucionar.
Solo uso printf () para USART para depurar mis programas escritos en C18.
fuente
La mayoría de los argumentos en contra de habilitar la optimización en su compilación se reducen a:
En mi humilde opinión, los dos primeros son legítimos, el tercero no tanto. A menudo significa que tiene un código incorrecto o depende de una explotación insegura del lenguaje / implementación o tal vez el autor es solo un fanático del buen comportamiento indefinido del tío.
El blog Embedded in Academia tiene una o dos cosas que decir sobre el comportamiento indefinido, y esta publicación trata sobre cómo los compiladores lo explotan: http://blog.regehr.org/archives/761
fuente