He leído Programación de microcontroladores PIC de 16 bits en C , y hay esta afirmación en el libro:
Sin embargo, durante las fases de desarrollo y depuración de un proyecto, siempre es una buena práctica deshabilitar todas las optimizaciones, ya que podrían modificar la estructura del código que se está analizando y hacer que la colocación de un solo paso y el punto de interrupción sean problemáticas.
Confieso que estaba un poco confundido. No entendí si el autor dijo eso debido al período de evaluación del C30 o si es realmente una buena práctica.
Me gustaría saber si realmente usas esta práctica y por qué.
Esto es bastante estándar en la ingeniería de software en general: cuando optimiza el código, el compilador puede reorganizar las cosas como quiera, 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 ciclo y nunca cambia la variable dentro del ciclo, el optimizador puede mover esa inicialización fuera del ciclo, para que no pierda tiempo con eso.
También puede darse cuenta de que calcula un número con el que luego no hace nada antes de sobrescribirlo. En ese caso, podría eliminar el cálculo inútil.
El problema con la optimización es que querrá 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 usted quiere (generalmente, colocará el punto de interrupción en algún lugar cercano). Por lo tanto, para hacer que el código generado se asemeje más a lo que escribió, desactive las optimizaciones durante la depuración; esto asegura que el código que desea romper realmente esté allí.
Sin embargo, debe tener cuidado con esto, ya que, dependiendo de su código, ¡la optimización puede romper cosas! En general, el código que es roto por un optimizador que funciona correctamente es realmente un código con errores que se está saliendo 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ó:
Daniel,
Prefiero depurar usando cualquier optimización que haya en el código publicado. La NASA dice "prueba lo que vuelas, vuela lo que pruebas". En otras palabras, ¡no haga pruebas y luego cambie el código!
Sin embargo, a veces uno tiene que desactivar las optimizaciones para que el depurador funcione. Intento apagarlos solo en los módulos en los que estoy trabajando. Por esa razón, creo en mantener archivos pequeños, digamos unos cientos de líneas de código más o menos.
Todo lo mejor, Jack
Depende, y esto es generalmente cierto para todas las herramientas, no solo para 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 reemplazarse con una serie de desplazamientos a la izquierda, etc. aunque este último tipo de optimización por lo general todavía se puede realizar paso a paso, es principalmente la reestructuración de la declaración de control lo que te atrapa.
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 es posible que la instrucción que está rompiendo ya no exista. Por ejemplo, puede intentar establecer un punto de interrupción dentro de una declaración if, pero el compilador puede haberlo eliminado. Puede intentar establecer un punto de interrupción dentro de un ciclo while o for, pero el compilador decidió desenrollar ese ciclo para que ya no exista.
Por esta razón, si puede depurar con las optimizaciones desactivadas, suele ser más fácil. Siempre debe volver a realizar la prueba con las optimizaciones activadas. Esta es la única forma en que descubrirá que se perdió algo 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, por ejemplo, algunas interrupciones. En estos casos, debe codificar los bits críticos en ensamblador o usar 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 fijo en el peor de los casos.
El otro problema puede ser encajar el código en el uC, es posible que necesite optimizaciones de densidad de código para simplemente encajar su código en el chip. Esta es una de las razones por las que generalmente es una buena idea comenzar con la mayor capacidad de ROM uC en una familia y solo elegir una más pequeña para la fabricación, después de que su código esté bloqueado.
En general, depuraría con cualquier configuración con la que planee lanzar. Si fuera a lanzar código optimizado, depuraría con código optimizado. Si iba a publicar código no optimizado, depuraría con código no optimizado. Hago esto por dos razones. Primero, los optimizadores pueden generar 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 a los del código no optimizado. Como resultado, me gusta obtener la mayor cantidad de tiempo de prueba posible con cualquier configuración con la que planee lanzar.
Dicho esto, los optimizadores pueden dificultar la depuración, como se indicó en las respuestas anteriores. Si encuentro una sección particular de código que es difícil de depurar, apagaré temporalmente el optimizador, haré la depuración para que el código funcione, luego volveré a encender el optimizador y probaré una vez más.
Mi estrategia normal es desarrollar con la optimización final (máximo para el tamaño o la velocidad, según corresponda), pero desactivar la optimización temporalmente si necesito depurar o rastrear algo. Esto reduce el riesgo de que surjan errores como resultado de cambiar 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 las variables como volátiles donde sea necesario; esto es esencial para decirle al compilador qué cosas no deben "optimizarse".
ciertamente tiene sentido en el caso de los puntos de interrupción... ya que el compilador puede eliminar muchas declaraciones que en realidad no afectan la memoria.
considera algo como:
int i =0;
for (int j=0; j < 10; j++)
{
i+=j;
}
return 0;
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:
for (int j=delay; j != 0; j--)
{
asm( " nop " );
asm( " nop " );
}
return 0;
Use cualquier forma con la que vaya a lanzar, depuradores y compilación para depurar, oculte muchos (MUCHOS) errores que no ve hasta que compila para el lanzamiento. Para entonces, es mucho más difícil encontrar esos errores, en comparación con la depuración sobre la marcha. Hace 20 años y nunca tuve un uso para un gdb u otro depurador, no es necesario mirar variables o un solo paso. Cientos a miles de líneas por día. Entonces 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 requerir el doble o más del doble de esfuerzo. Si se encuentra en un aprieto 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, como 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 los optimizadores cometen errores y algunos no tienen fe en eso. Si ese es el caso, no hay nada de malo en que se compile sin optimización, solo hágalo de esa manera todo el tiempo. La ruta que prefiero es compilar para la optimización, entonces, si sospecho que hay un problema con el compilador, deshabilite la optimización si eso lo soluciona, por lo general, a veces examina la salida del ensamblador para averiguar por qué.
Siempre desarrollo código con -O0 (opción gcc para desactivar la optimización). Cuando sienta que estoy en el punto en el que quiero comenzar a dejar que las cosas se dirijan más hacia un lanzamiento, comenzaré con -Os (optimizar para el tamaño) ya que, en general, cuanto más código pueda mantener en caché, mejor será. incluso si no está súper optimizado.
Encuentro 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. Es una educación bastante interesante a veces, y también puede descubrir errores del compilador... ¡esas cosas desagradables que te hacen tirarte de los pelos tratando de descubrir qué está mal con tu código!
Si realmente lo necesito, comenzaré a agregar -fdata-sections y -fcode-sections con --gc-sections, que permiten que el enlazador elimine funciones completas y segmentos de datos que en realidad no se usan. Hay muchas cosas pequeñas con las que puedes jugar para intentar reducir las cosas aún más o hacerlas más rápidas, 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 la daré. -armar.
Sí, deshabilitar las optimizaciones durante la depuración ha sido la mejor práctica desde hace un tiempo, por tres razones:
Muchas personas van aún más lejos en esta dirección y envían con afirmaciones activadas .
Simple: las optimizaciones consumen mucho tiempo y pueden ser inútiles si tiene que cambiar ese fragmento de código más adelante en el desarrollo. Así que bien 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 necesitarán cambios.
Si está utilizando el depurador, deshabilitaría las optimizaciones y habilitaría la depuración.
Personalmente, encuentro que el depurador de PIC causa más problemas de los que me ayuda a solucionar.
Solo uso printf() para USART para depurar mis programas escritos en C18.
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 confía en 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 un par de 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
kevin vermeer
daniel grillo
Marca
Michael Kohne
kevin vermeer
Kortuk
Kortuk