Est-ce vraiment une bonne pratique pour désactiver les optimisations pendant les phases de développement et de débogage?

15

J'ai lu Programmation de microcontrôleurs PIC 16 bits en C , et il y a cette affirmation dans le livre:

Pendant les phases de développement et de débogage d'un projet, cependant, il est toujours recommandé de désactiver toutes les optimisations car elles pourraient modifier la structure du code analysé et rendre problématique le placement en une seule étape et le point d'arrêt.

J'avoue que j'étais un peu confus. Je n'ai pas compris si l'auteur avait dit cela à cause de la période d'évaluation du C30 ou si c'était vraiment une bonne pratique.

Je voudrais savoir si vous utilisez réellement cette pratique et pourquoi?

Daniel Grillo
la source

Réponses:

16

C'est assez standard dans l'ingénierie logicielle dans son ensemble - lorsque vous optimisez le code, le compilateur est autorisé à réorganiser les choses à peu près comme il le souhaite, tant que vous ne pouvez pas faire de différence de fonctionnement. Ainsi, par exemple, si vous initialisez une variable à l'intérieur de chaque itération d'une boucle et que vous ne changez jamais la variable à l'intérieur de la boucle, l'optimiseur est autorisé à déplacer cette initialisation hors de la boucle, afin de ne pas perdre de temps avec.

Il peut également se rendre compte que vous calculez un nombre avec lequel vous ne faites rien avant d'écraser. Dans ce cas, cela pourrait éliminer le calcul inutile.

Le problème avec l'optimisation est que vous voudrez mettre un point d'arrêt sur un morceau de code que l'optimiseur a déplacé ou éliminé. Dans ce cas, le débogueur ne peut pas faire ce que vous voulez (généralement, il mettra le point d'arrêt quelque part près). Ainsi, pour que le code généré ressemble davantage à ce que vous avez écrit, vous désactivez les optimisations pendant le débogage - cela garantit que le code que vous souhaitez utiliser est vraiment là.

Cependant, vous devez être prudent avec cela, car en fonction de votre code, l'optimisation peut casser les choses! En général, le code qui est cassé par un optimiseur fonctionnant correctement est vraiment juste du code bogué qui s'en sort avec quelque chose, donc vous voulez généralement comprendre pourquoi l'optimiseur le casse.

Michael Kohne
la source
5
Le contrepoint de cet argument est que l'optimiseur rendra probablement les choses plus petites et / ou plus rapides, et si vous avez du code limité dans le temps ou la taille, vous pouvez casser quelque chose en désactivant l'optimisation et perdre votre temps à déboguer un problème qui ne se produit pas. n'existe pas vraiment. Bien sûr, le débogueur peut également rendre votre code plus lent et plus volumineux.
Kevin Vermeer
Où puis-je en savoir plus à ce sujet?
Daniel Grillo
Je n'ai pas travaillé avec le compilateur C30 mais pour le compilateur C18, il y avait une note d'application / manuel pour le compilateur qui couvrait les optimisations qu'il supportait.
Mark
@O Engenheiro: consultez les documents de votre compilateur pour connaître les optimisations qu'il prend en charge. L'optimisation varie énormément selon le compilateur, les bibliothèques et l'architecture cible.
Michael Kohne
Encore une fois, pas pour le compilateur C30, mais gcc publie une longue liste des différentes optimisations qui peuvent être appliquées. Vous pouvez également utiliser cette liste pour obtenir une optimisation fine, au cas où vous auriez une structure de contrôle particulière que vous souhaitez conserver intacte. La liste est ici: gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Kevin Vermeer
7

J'ai envoyé cette question à Jack Ganssle et voici ce qu'il m'a répondu:

Daniel,

Je préfère déboguer en utilisant les optimisations qui figureront dans le code publié. La NASA dit "testez ce que vous pilotez, pilotez ce que vous testez". En d'autres termes, ne faites pas de test, puis changez le code!

Cependant, il faut parfois désactiver les optimisations pour que le débogueur fonctionne. J'essaie de les désactiver uniquement dans les modules sur lesquels je travaille. Pour cette raison, je crois qu'il faut garder les fichiers petits, disons quelques centaines de lignes de code environ.

Cordialement, Jack

Daniel Grillo
la source
Je pense qu'il y a une distinction non déclarée entre les deux paragraphes de cette réponse. Les tests se réfèrent à une procédure qui est censée démontrer que les articles souples, fermes et / ou durs fonctionnent correctement. Le débogage est le processus dans lequel le code est parcouru instruction par instruction pour voir pourquoi il ne fonctionne pas [encore].
Kevin Vermeer
C'est agréable d'avoir le choix. Ainsi, les tests peuvent couvrir plus de variétés / permutations avec et sans optimisation. Plus la couverture est grande, mieux c'est les tests
@reemrevnivek, lorsque vous déboguez, ne testez-vous pas aussi?
Daniel Grillo
@O Engenheiro - Non. Je ne débogue que si le test échoue.
Kevin Vermeer
6

Cela dépend, et cela est généralement vrai pour tous les outils, pas seulement C30.

Les optimisations suppriment et / ou restructurent souvent le code de diverses manières. Votre instruction switch peut être réimplémentée avec une construction if / else ou dans certains cas peut être supprimée tous ensemble. y = x * 16 peut être remplacé par une série de décalages à gauche, etc. bien que ce dernier type d'optimisation puisse généralement encore être franchi, c'est principalement la restructuration de la déclaration de contrôle qui y parvient.

Cela peut rendre impossible le passage d'un débogueur dans votre code C car les structures que vous avez définies en C n'existent plus, elles ont été remplacées ou réorganisées par le compilateur en quelque chose que le compilateur pense être plus rapide ou utiliser moins d'espace. Cela peut également rendre les points d'arrêt impossibles à définir à partir de la liste C, car l'instruction sur laquelle vous appuyez peut ne plus exister. Par exemple, vous pouvez essayer de définir un point d'arrêt dans une instruction if, mais le compilateur peut l'avoir supprimé if. Vous pouvez essayer de définir un point d'arrêt à l'intérieur d'une boucle while ou for mais le compilateur a décidé de dérouler cette boucle afin qu'elle n'existe plus.

Pour cette raison, si vous pouvez déboguer avec des optimisations désactivées, c'est généralement plus facile. Vous devez toujours retester avec les optimisations activées. C'est à peu près la seule façon de découvrir que vous avez raté un important volatileet que cela provoque des échecs intermittents (ou une autre bizarrerie).

Dans le cas du développement intégré, vous devez quand même faire attention aux optimisations. Plus précisément dans les sections de code dont le timing est critique, certaines interruptions par exemple. Dans ces cas, vous devez soit coder les bits critiques de l'assembly, soit utiliser des directives de compilation pour vous assurer que ces sections ne sont pas optimisées afin que vous sachiez qu'elles ont un temps d'exécution fixe ou un temps d'exécution du pire cas fixe.

L'autre gotcha peut être l'adaptation de code dans l'UC, vous pouvez avoir besoin d'optimisations de densité de code pour adapter simplement votre code dans la puce. C'est une des raisons pour lesquelles c'est généralement une bonne idée de commencer avec la plus grande capacité de ROM uC d'une famille et de n'en choisir qu'une plus petite pour la fabrication, une fois votre code verrouillé.

marque
la source
5

En règle générale, je déboguerais avec les paramètres avec lesquels je prévoyais de publier. Si je devais publier du code optimisé, je déboguerais avec du code optimisé. Si je devais libérer du code non optimisé, je déboguerais avec du code non optimisé. Je le fais pour deux raisons. Premièrement, les optimiseurs peuvent faire des différences de synchronisation suffisamment importantes pour que le produit final se comporte différemment du code non optimisé. Deuxièmement, même si la plupart sont assez bons, les éditeurs de compilateurs font des erreurs et le code optimisé peut produire des résultats différents à partir du code non optimisé. En conséquence, j'aime obtenir autant de temps de test que possible avec le paramètre avec lequel je prévois de sortir.

Cela étant dit, les optimiseurs peuvent rendre le débogage difficile, comme indiqué dans les réponses précédentes. Si je trouve une section de code particulière qui est difficile à déboguer, je désactiverai temporairement l'optimiseur, je ferai le débogage pour que le code fonctionne, puis je réactiverai l'optimiseur et je testerai à nouveau.

semaj
la source
1
Mais une seule étape du code peut être presque impossible avec les optimisations activées. Déboguez avec les optimisations désactivées et exécutez vos tests unitaires avec le code de version.
Rocketmagnet
3

Ma stratégie normale est de développer avec l'optimisation finale (max pour la taille ou la vitesse selon le cas), mais désactivez temporairement l'optimisation si j'ai besoin de déboguer ou de tracer quelque chose. Cela réduit le risque de bogues apparaissant en raison de la modification des niveaux d'optimisation.

Un mode d'échec typique est lorsque l'augmentation de l'optimisation fait apparaître des bogues précédemment invisibles du fait que vous n'avez pas déclaré les variables comme volatiles si nécessaire - cela est essentiel pour dire au compilateur quelles choses ne doivent pas être `` optimisées ''.

mikeselectricstuff
la source
2

Utilisez la forme que vous allez publier, les débogueurs et la compilation pour le débogage cachent beaucoup (BEAUCOUP) de bogues que vous ne voyez pas jusqu'à ce que vous compiliez pour la sortie. D'ici là, il est beaucoup plus difficile de trouver ces bogues, par rapport au débogage au fur et à mesure. 20 ans quelque chose maintenant et je n'ai jamais eu d'utilisation d'un gdb ou d'un autre débogueur comme, pas besoin de regarder des variables ou une seule étape. Des centaines à des milliers de lignes par jour. Il est donc possible, ne soyez pas amené à penser le contraire.

La compilation pour le débogage puis la compilation ultérieure pour la publication peut et prendra deux à plus de deux fois l'effort. Si vous entrez dans une liaison et devez utiliser un outil comme un débogueur, puis compilez pour que le débogueur résout le problème spécifique, puis revenez au fonctionnement normal.

D'autres problèmes sont également vrais comme l'optimiseur rend le code plus rapide, donc pour incorporer en particulier vos changements de timing avec les options du compilateur et qui peuvent affecter les fonctionnalités de votre programme, utilisez ici encore le choix de compilation livrable pendant toute la phase. Les compilateurs sont aussi des programmes et ont des bugs et les optimiseurs font des erreurs et certains n'y croient pas. Si c'est le cas, il n'y a rien de mal à compiler sans optimisation, faites-le de cette façon tout le temps. Le chemin que je préfère est de compiler pour l'optimisation, puis si je soupçonne un problème de compilateur, désactivez l'optimisation si cela le corrige, généralement en va-et-vient, en examinant parfois la sortie de l'assembleur pour comprendre pourquoi.

old_timer
la source
1
+1 juste pour développer votre bonne réponse: souvent, la compilation en mode «débogage» remplira la pile / le tas autour des variables avec un espace non alloué pour atténuer les petites erreurs d'écriture et de formatage des chaînes. Vous obtiendrez plus souvent un bon crash d'exécution si vous compilez en version.
Morten Jensen
2

Je développe toujours du code avec -O0 (option gcc pour désactiver l'optimisation). Lorsque je sens que je suis au point où je veux commencer à laisser les choses se diriger davantage vers une version, je commence par -Os (optimiser la taille), car généralement plus vous pouvez conserver de code dans le cache, mieux ce sera, même si ce n'est pas optimisé super-duper.

Je trouve que gdb fonctionne beaucoup mieux avec le code -O0, et c'est beaucoup plus facile à suivre si vous devez entrer dans l'assemblage. Le basculement entre -O0 et -Os vous permet également de voir ce que le compilateur fait à votre code. C'est parfois une éducation assez intéressante, et peut également découvrir des bogues de compilation ... ces choses désagréables qui vous font vous arracher les cheveux en essayant de comprendre ce qui ne va pas avec votre code!

Si j'en ai vraiment besoin, je vais commencer à ajouter des sections -fdata et -fcode-sections avec --gc-sections, qui permettent à l'éditeur de liens de supprimer des fonctions et des segments de données entiers qui ne sont pas réellement utilisés. Il y a beaucoup de petites choses avec lesquelles vous pouvez bricoler pour essayer de réduire davantage ou d'accélérer les choses, mais dans l'ensemble ce sont les seules astuces que je finis par utiliser, et tout ce qui doit être plus petit ou plus rapide, je vais le remettre -assembler.

akohlsmith
la source
2

Oui, la désactivation des optimisations pendant le débogage est la meilleure pratique depuis un certain temps maintenant, pour trois raisons:

  • (a) si vous allez exécuter le programme en une seule étape avec un débogueur de haut niveau, c'est un peu moins déroutant.
  • (a) (obsolète) si vous allez déboguer en une seule étape le programme avec un débogueur en langage assembleur, c'est beaucoup moins déroutant. (Mais pourquoi vous dérangeriez-vous avec cela alors que vous pourriez utiliser un débogueur de haut niveau?)
  • (b) (obsolète depuis longtemps), vous n'allez probablement exécuter cet exécutable qu'une seule fois, puis apporter des modifications et recompiler. C'est une perte de temps pour une personne d'attendre 10 minutes supplémentaires pendant que le compilateur "optimise" cet exécutable particulier, lorsque cela va économiser moins de 10 minutes d'exécution. (Cela n'est plus pertinent avec les PC modernes qui peuvent compiler un exécutable de microcontrôleur typique, avec une optimisation complète, en moins de 2 secondes).

Beaucoup de gens vont encore plus loin dans cette direction et expédient avec des assertions activées .

davidcary
la source
Une seule étape dans le code assembleur peut être très utile pour diagnostiquer les cas où le code source spécifie réellement autre chose que ce à quoi il ressemble (par exemple "longvar1 & = ~ 0x40000000; longvar2 & = ~ 0x80000000;")) ou lorsqu'un compilateur génère du code bogué . J'ai repéré certains problèmes en utilisant des débogueurs de code machine que je ne pense vraiment pas que j'aurais pu localiser autrement.
supercat
2

Simple: les optimisations prennent du temps et peuvent être inutiles si vous devez modifier ce morceau de code plus tard dans le développement. Ils peuvent donc être une perte de temps et d'argent.
Ils sont cependant utiles pour les modules finis; des parties du code qui n'auront probablement plus besoin de modifications.

stevenvh
la source
2

cela a certainement du sens dans le cas des points d'arrêt ... car le compilateur peut supprimer de nombreuses instructions qui n'affectent pas réellement la mémoire.

considérez quelque chose comme:

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

pourrait être entièrement optimisé (car il in'est jamais lu). il semblerait, du point de vue de votre point d'arrêt, qu'il a ignoré tout ce code, alors qu'il n'était tout simplement pas là ... Je suppose que c'est pourquoi dans les fonctions de type veille, vous verrez souvent quelque chose comme:

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;
Joueur Grady
la source
1

Si vous utilisez le débogueur, je désactiverais les optimisations et activerais le débogage.

Personnellement, je trouve que le débogueur PIC provoque plus de problèmes qu'il ne m'aide à résoudre.
J'utilise simplement printf () pour USART pour déboguer mes programmes écrits en C18.

mjh2007
la source
1

La plupart des arguments contre l'activation de l'optimisation dans votre compilation se résument à:

  1. problème avec le débogage (connectivité JTAG, points d'arrêt, etc.)
  2. mauvais timing logiciel
  3. sh * t cesse de fonctionner correctement

À mon humble avis, les deux premiers sont légitimes, le troisième moins. Cela signifie souvent que vous avez un mauvais code ou que vous comptez sur une exploitation non sécurisée du langage / de l'implémentation ou que l'auteur est peut-être juste un fan du bon vieux comportement de l'oncle non défini.

Le blog Embedded in Academia a une ou deux choses à dire sur le comportement indéfini, et cet article explique comment les compilateurs l'exploitent: http://blog.regehr.org/archives/761

Morten Jensen
la source
Une autre raison possible est que le compilateur peut être extrêmement lent lorsque les optimisations sont activées.
supercat