¿Es realmente una buena práctica desactivar las optimizaciones durante las fases de desarrollo y depuración?

15

He leído Programación de microcontroladores PIC de 16 bits en C , y hay una afirmación en el libro :

  

Durante el desarrollo y depuración   fases de un proyecto, sin embargo, es   Siempre es una buena práctica deshabilitar todo.   optimizaciones ya que podrían modificar el   Estructura del código que se está analizando.   y hacer un solo paso y   problemática en la ubicación del punto de corte.

Confieso que estaba un poco confundido. No entendí si el autor dijo eso debido al período de evaluación C30 o si es realmente una buena práctica.

Me gustaría saber si realmente utiliza esta práctica, y por qué?

    
pregunta Daniel Grillo

12 respuestas

16

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 prácticamente como lo desee, siempre y cuando 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 tiempo con él.

También podría darse cuenta de que calcula un número con el que no hace nada antes de sobreescribir. 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 ha movido o eliminado. En ese caso, el depurador no puede hacer lo que usted quiere (generalmente, cerrará el punto de interrupción en algún lugar). Por lo tanto, para hacer que el código generado se parezca más a lo que escribiste, desactivas las optimizaciones durante la depuración, esto asegura que el código que deseas interrumpir realmente está 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 un código con errores que se está saliendo con la suya, por lo que normalmente querrá averiguar por qué el optimizador lo rompe.

    
respondido por el Michael Kohne
7

Le envié esta pregunta a Jack Ganssle y esto es lo que me respondió:

  

Daniel,

     

Prefiero depurar utilizando las optimizaciones que se encuentren en el código publicado. La NASA dice "prueba lo que vuelas, vuela lo que pruebas". En otras palabras, ¡no hagas pruebas y luego cambies 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 los archivos pequeños, digamos unos cientos de líneas de código o algo así.

     

Todo lo mejor,   Jack

    
respondido por el Daniel Grillo
6

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 ser reemplazado por una serie de turnos a la izquierda, etc. Aunque este último tipo de optimización por lo general todavía puede ser superado, es principalmente la reestructuración de la declaración de control lo que lo hace.

Esto puede hacer que sea imposible pasar a 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 que usará menos espacio. También puede hacer que los puntos de interrupción sean imposibles de establecer desde la lista de C, ya que es posible que la instrucción de interrupción ya no exista. Por ejemplo, puede intentar establecer un punto de interrupción dentro de una sentencia if, pero el compilador puede haber eliminado eso si. 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 puedes depurar con las optimizaciones desactivadas, generalmente es más fácil. Siempre debe volver a probar con optimizaciones en. Esta es casi la única forma en que descubrirá que se perdió un volatile importante y que causa 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 directivas de compilación 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 en el peor de los casos.

El otro gotcha puede ser el ajuste de código en la unidad de control, 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, por lo general, es una buena idea comenzar con la capacidad de ROM más grande en una familia y solo elegir una más pequeña para la fabricación, una vez que su código esté bloqueado.

    
respondido por el Mark
5

En general, depuraría con cualquier configuración que planeaba lanzar con. Si fuera a lanzar código optimizado, depuraría con código optimizado. Si fuera a liberar código no optimizado, depuraría con código no optimizado. Hago esto por dos razones. Primero, los optimizadores pueden hacer diferencias de tiempo suficientemente significativas 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 la mayor cantidad de tiempo de prueba posible con cualquier configuración con la que planeo publicar.

Dicho esto, los optimizadores pueden dificultar la depuración, como se señaló en las respuestas anteriores. Si encuentro una sección particular del 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.

    
respondido por el semaj
3

Mi estrategia normal es desarrollar con la optimización final (máx. para el tamaño o la velocidad, según corresponda), pero desactive la optimización temporalmente si necesito depurar o rastrear algo. Esto reduce el riesgo de que surjan errores como resultado de los cambios en los niveles de optimización.

Un modo de falla típico es cuando el aumento de la optimización hace que aparezcan errores no vistos anteriormente debido a que no ha declarado que las variables son volátiles cuando sea necesario. Esto es esencial para decirle al compilador qué cosas no deben "optimizarse".

    
respondido por el mikeselectricstuff
2

ciertamente tiene sentido en el caso de puntos de interrupción ... ya que el compilador puede eliminar muchas declaraciones que 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, vería que omitió todo ese código, cuando básicamente no estaba allí ... Supongo que es por eso que en las funciones de tipo de sueño a menudo verá algo como:

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;
    
respondido por el Grady Player
2

Use la forma con la que va a realizar la liberación, los depuradores y la compilación para la depuración ocultan muchos (A MUCHO) errores que no ve hasta que compile para su publicación. Para entonces es mucho más difícil encontrar esos errores, ya que depurando sobre la marcha. Hace algo 20 años y nunca tuve un uso para un gdb u otro depurador similar, sin necesidad de ver variables o un solo paso. Cientos a miles de líneas por día. Así que es posible, no te dejes pensar de otra manera.

La compilación para la depuración y luego la compilación para el lanzamiento puede y llevará dos o más veces el doble de esfuerzo. Si se enreda y tiene que usar una herramienta como un depurador, compile para que el depurador resuelva el problema específico y luego vuelva al funcionamiento normal.

Otros problemas también son verdaderos, ya que el optimizador hace que el código sea más rápido, por lo que para incrustar en particular los cambios de tiempo con las opciones del compilador y que pueden afectar la funcionalidad de su programa, nuevamente utilice la opción de compilación entregable durante toda la fase. Los compiladores son programas también y tienen errores y optimizadores que cometen errores y algunos no tienen ninguna fe en eso. Si ese es el caso, no hay nada de malo en la compilación sin optimización, solo hazlo así todo el tiempo. El camino que prefiero es compilar para la optimización, entonces, si sospecho que un problema del compilador desactiva la optimización, si eso lo corrige, generalmente va y viene a veces examinando la salida del ensamblador para averiguar por qué.

    
respondido por el old_timer
2

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 dejar que las cosas se dirijan más hacia un lanzamiento, comenzaré con -Os (optimizar para el tamaño) ya que generalmente cuanto más código pueda mantener en el caché, mejor será, incluso si no está super-duper optimizado.

Encuentro que gdb funciona mucho mejor con el código -O0, y es mucho más fácil de seguir si tienes que ingresar al ensamblaje. Alternar entre -O0 y -Os también le permite ver lo que el compilador está haciendo con su código. Es una educación bastante interesante a veces, y también puede descubrir errores en el compilador ... ¡esas cosas desagradables que te hacen arrancarte el pelo tratando de descubrir qué es lo que está mal con tu código!

Si realmente lo necesito, comenzaré a agregar -fdata-secciones y -fcode-secciones con --gc-secciones, que permiten que el vinculador elimine funciones completas y segmentos de datos que no se usan realmente. Hay muchas cosas pequeñas con las que puedes jugar para tratar de reducirlas aún más o hacerlas más rápidas, pero en general estos son los únicos trucos que acabo de usar, y cualquier cosa que tenga que ser más pequeña o más rápida, te la daré. -construir.

    
respondido por el akohlsmith
2

Sí, deshabilitar las optimizaciones durante la depuración ha sido la mejor práctica desde hace un tiempo, por tres razones:

  • (a) Si vas a un solo paso del programa con un depurador de alto nivel, es un poco menos confuso.
  • (a) (obsoleto) si va a depurar el programa en un solo paso con un depurador en lenguaje ensamblador, es mucho menos confuso. (Pero, ¿por qué se molesta con esto cuando podría estar usando un depurador de alto nivel?)
  • (b) (obsoleto) probablemente solo ejecutará este ejecutable en particular una vez, luego realice algunos cambios y vuelva a compilarlo. Es una pérdida de tiempo que una persona espere 10 minutos adicionales mientras el compilador "optimiza" este ejecutable en particular, cuando eso ahorra menos de 10 minutos de tiempo de ejecución. (Esto ya no es relevante con las PC modernas que pueden compilar un ejecutable de microcontrolador típico, con optimización completa, en menos de 2 segundos).

Muchas personas van aún más lejos en esta dirección, y enviar con las aserciones activadas .

    
respondido por el davidcary
2

Simple: las optimizaciones requieren mucho tiempo y pueden ser inútiles si tiene que cambiar esa parte del 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.

    
respondido por el stevenvh
1

Si está utilizando el depurador, deshabilitaré las optimizaciones y habilitaré 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.

    
respondido por el mjh2007
1

La mayoría de los argumentos en contra de habilitar la optimización en su compilación se reducen a:

  1. problemas con la depuración (conectividad JTAG, puntos de interrupción, etc.)
  2. sincronización incorrecta del software
  3. sh * t deja de funcionar correctamente

En mi humilde opinión, los dos primeros son legítimos, el tercero no tanto. A menudo significa que tiene algún código incorrecto o confía en una explotación no segura del lenguaje / implementación o tal vez el autor es simplemente un fanático de la buena conducta indefinida del tío.

El blog de Embedded in Academia tiene una o dos cosas que decir sobre el comportamiento indefinido, y esta publicación es sobre cómo los compiladores la explotan: enlace

    
respondido por el Morten Jensen

Lea otras preguntas en las etiquetas