Différences de performances entre les versions de débogage et de publication

Je dois avouer que généralement, je n’ai pas pris la peine de basculer entre les configurations Debug et Release dans mon programme, et j’ai généralement opté pour la configuration Debug , même lorsque les programmes sont effectivement déployés chez les clients.

Pour autant que je sache, la seule différence entre ces configurations si vous ne le modifiez pas manuellement est que Debug a la constante DEBUG définie et que Release a vérifié le code Optimize .

Donc, mes questions sont en fait de deux ordres:

  1. Y a-t-il beaucoup de différences de performances entre ces deux configurations? Existe-t-il un type de code spécifique qui entraînera de grandes différences de performances, ou est-ce que ce n’est pas vraiment important?

  2. Existe-t-il un type de code qui s’exécute correctement sous la configuration de débogage qui pourrait échouer sous la configuration de la version ou pouvez-vous être certain que le code testé et fonctionnant correctement dans la configuration de débogage fonctionnera également correctement dans la configuration de la version.

Le compilateur C # lui-même ne modifie pas beaucoup l’IL émise dans la version Release. Notable est qu’il n’émet plus les opcodes NOP qui vous permettent de définir un point d’arrêt sur une accolade. Le grand est l’optimiseur intégré au compilateur JIT. Je sais qu’il fait les optimisations suivantes:

  • Méthode inlining. Un appel de méthode est remplacé par l’injection du code de la méthode. C’est un gros problème, cela rend les accesseurs de propriété essentiellement gratuits.

  • Allocation du registre CPU. Les variables locales et les arguments de méthode peuvent restr stockés dans un registre de processeur sans jamais être (ou moins fréquemment) stockés dans le cadre de la stack. Ceci est un grand, remarquable pour rendre le débogage du code optimisé si difficile. Et donner un sens au mot-clé volatile .

  • Elimination des index de tableau. Une optimisation importante lorsque vous travaillez avec des tableaux (toutes les classes de collections .NET utilisent un tableau en interne). Lorsque le compilateur JIT peut vérifier qu’une boucle n’indexe jamais un tableau hors limites, cela élimine la vérification d’index. Un gros.

  • Boucle déroulante. Les boucles avec de petits corps sont améliorées en répétant le code jusqu’à 4 fois dans le corps et en bouclant moins. Réduit le coût de la twig et améliore les options d’exécution super scalaire du processeur.

  • Élimination du code mort. Une instruction comme si (false) {/ /} est complètement éliminée. Cela peut se produire en raison d’un pliage et d’un alignement constants. Dans d’autres cas, le compilateur JIT peut déterminer que le code n’a aucun effet secondaire possible. Cette optimisation est ce qui rend le code de profilage si difficile.

  • Code de levage. Le code dans une boucle qui n’est pas affectée par la boucle peut être déplacé hors de la boucle. L’optimiseur d’un compilateur C passera beaucoup plus de temps à trouver des opportunités de levage. Il s’agit toutefois d’une optimisation coûteuse en raison de l’parsing du stream de données requirejse, et le gigue ne peut pas se permettre le temps nécessaire pour ne citer que des cas évidents. Forcer les programmeurs .NET à écrire de meilleurs codes source et se hisser.

  • Élimination des sous-expressions communes. x = y + 4; z = y + 4; devient z = x; Assez commun dans les instructions comme dest [ix + 1] = src [ix + 1]; écrit pour la lisibilité sans introduire de variable auxiliaire. Pas besoin de compromettre la lisibilité.

  • Pliage constant x = 1 + 2; devient x = 3; Cet exemple simple est détecté très tôt par le compilateur, mais il se produit à l’heure JIT lorsque d’autres optimisations rendent cela possible.

  • Propagation de la copie. x = a; y = x; devient y = a; Cela permet à l’allocateur de registre de prendre de meilleures décisions. C’est une grosse affaire dans la gigue x86 car il y a peu de registres avec lesquels travailler. Il est essentiel de le sélectionner pour choisir les bons.

Ce sont des optimisations très importantes qui peuvent faire une grande différence lorsque, par exemple, vous profilez la version Debug de votre application et la comparez à la version Release. Cela n’a d’importance que lorsque le code est sur votre chemin critique, les 5 à 10% du code que vous écrivez affectent réellement la performance de votre programme. L’optimiseur JIT n’est pas assez intelligent pour savoir ce qu’il y a de plus important, il ne peut appliquer que la numérotation “tourner à onze” pour tout le code.

Le résultat effectif de ces optimisations sur le temps d’exécution de votre programme est souvent affecté par le code exécuté ailleurs. Lire un fichier, exécuter une requête dbase, etc. Rendre le travail de l’optimiseur JIT complètement invisible. Ca ne dérange pas si 🙂

L’optimiseur JIT est un code assez fiable, principalement parce qu’il a été mis à l’épreuve des millions de fois. Il est extrêmement rare d’avoir des problèmes dans la version de compilation de votre programme. Cela arrive cependant. Les jitters x64 et x86 ont eu des problèmes avec les structures. La gigue x86 présente des problèmes de cohérence en virgule flottante, produisant des résultats subtilement différents lorsque les intermédiaires d’un calcul à virgule flottante sont conservés dans un registre FPU avec une précision de 80 bits au lieu d’être tronqués lorsqu’ils sont vidés en mémoire.

  1. Oui, il existe de nombreuses différences de performances et celles-ci s’appliquent vraiment à tout votre code. Le débogage n’optimise que très peu les performances et le mode de publication.

  2. Seul le code qui repose sur la constante DEBUG peut fonctionner différemment avec une version release. En plus de cela, vous ne devriez pas voir de problèmes.

Un exemple de code d’ DEBUG qui dépend de la constante DEBUG est la méthode Debug.Assert() , qui possède l’atsortingbut [Conditional("DEBUG)"] défini. Cela signifie qu’il dépend également de la constante DEBUG et que celle-ci n’est pas incluse dans la version de publication.

Cela dépend fortement de la nature de votre application. Si votre application est lourde en interface utilisateur, vous ne remarquerez probablement aucune différence puisque le composant le plus lent connecté à un ordinateur moderne est l’utilisateur. Si vous utilisez des animations d’interface utilisateur, vous pouvez tester si vous pouvez percevoir un retard notable lors de l’exécution de la version DEBUG.

Cependant, si vous avez beaucoup de calculs à forte charge de calcul, vous remarquerez des différences (pouvant atteindre 40%, comme mentionné dans @Pieter, mais cela dépend de la nature des calculs).

C’est fondamentalement un compromis de conception. Si vous publiez sous DEBUG, si les utilisateurs rencontrent des problèmes, vous pouvez obtenir une traçabilité plus significative et effectuer des diagnostics beaucoup plus flexibles. En libérant dans la version DEBUG, vous évitez également l’optimiseur produisant des Heisenbugs obscurs.

  • D’après mon expérience, les applications de taille moyenne ou supérieure sont nettement plus réactives dans une version Release. Essayez-le avec votre application et voyez ce que vous ressentez.

  • Une chose qui peut vous empiéter sur les builds de Release est que le code de construction Debug peut parfois supprimer les conditions de concurrence et autres bogues liés aux threads. Un code optimisé peut entraîner une réorganisation des instructions et une exécution plus rapide peut aggraver certaines conditions de course.

Vous ne devez jamais libérer un build .NET Debug en production. Il peut contenir du code moche pour la prise en charge de Edit-and-Continue ou qui sait quoi d’autre. Pour autant que je sache, cela ne se produit que dans VB et non C # (note: le message original est marqué C #) , mais il devrait quand même donner une raison de faire une pause sur ce que Microsoft est autorisé à faire avec une version Debug. En fait, avant .NET 4.0, le code VB fuit la mémoire proportionnellement au nombre d’instances d’objects avec les événements que vous construisez pour la prise en charge de la modification et de la poursuite. (Bien que cela soit signalé comme étant corrigé par https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging , le code généré semble méchant, créer des objects WeakReference et les append à une liste statique tout en maintenant un verrou ) Je ne veux certainement pas de ce type de support de débogage dans un environnement de production!

Dans mon expérience, la pire chose qui soit sortie du mode Release sont les obscurs “bugs de publication”. Puisque l’IL (langage intermédiaire) est optimisé en mode Release, il existe la possibilité de bogues qui ne se seraient pas manifestés en mode Debug. Il existe d’autres questions SO couvrant ce problème: Les raisons courantes des bogues dans la version de la version ne sont pas présentes en mode de débogage

Cela m’est arrivé une ou deux fois, où une simple application de console fonctionnerait parfaitement en mode Debug, mais avec la même entrée, une erreur se produirait en mode Release. Ces bogues sont extrêmement difficiles à déboguer (par définition du mode Release, ironiquement).

Je dirais que 1) dépend largement de votre mise en œuvre. Habituellement, la différence n’est pas énorme. J’ai fait beaucoup de mesures et souvent je ne pouvais pas voir une différence. Si vous utilisez du code non géré, de nombreuses baies volumineuses et autres, la différence de performance est légèrement plus importante, mais pas différente (comme en C ++). 2) Habituellement, dans le code de version, moins d’erreurs sont affichées (tolérance plus élevée), par conséquent un commutateur devrait fonctionner correctement.

  **Debug Mode:** Developer use debug mode for debugging the web application on live/local server. Debug mode allow developers to break the execution of program using interrupt 3 and step through the code. Debug mode has below features: 1) Less optimized code 2) Some additional instructions are added to enable the developer to set a breakpoint on every source code line. 3) More memory is used by the source code at runtime. 4) Scripts & images downloaded by webresource.axd are not cached. 5) It has big size, and runs slower. **Release Mode:** Developer use release mode for final deployment of source code on live server. Release mode dlls contain optimized code and it is for customers. Release mode has below features: More optimized code Some additional instructions are removed and developer can't set a breakpoint on every source code line. 1) Less memory is used by the source code at runtime. 2) Scripts & images downloaded by webresource.axd are cached. 3) It has small size, and runs fast. 4) Scripts & images downloaded by webresource.axd are cached. 5) It has small size, and runs fast.