Je dois admettre que, généralement, je n'ai pas pris la peine de basculer entre les configurations de débogage et de version dans mon programme, et j'ai généralement opté pour la configuration de débogage , même lorsque les programmes sont effectivement déployés chez le client.
Pour autant que je sache, la seule différence entre ces configurations si vous ne la modifiez pas manuellement est que Debug a la DEBUG
constante définie et Release a vérifié le code Optimize .
Donc, mes questions sont en fait doubles:
Existe-t-il de grandes 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 ici, ou n'est-ce pas vraiment si important?
Existe-t-il un type de code qui fonctionnera correctement sous la configuration de débogage qui pourrait échouer sous la configuration de version , ou pouvez-vous être certain que le code testé et fonctionnant correctement sous la configuration de débogage fonctionnera également correctement sous la configuration de version.
la source
Réponses:
Le compilateur C # lui-même n'altère pas beaucoup l'IL émis dans la version Release. Il convient de noter qu'il n'émet plus les opcodes NOP qui vous permettent de définir un point d'arrêt sur une accolade. Le gros est l'optimiseur intégré au compilateur JIT. Je sais que cela apporte les optimisations suivantes:
Intégration de la méthode. 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 de registre CPU. Les variables locales et les arguments de méthode peuvent rester stockés dans un registre CPU sans jamais (ou moins fréquemment) être stockés dans le cadre de la pile. C'est un gros problème, remarquable pour rendre le débogage du code optimisé si difficile. Et donner un sens au mot clé volatile .
Élimination de la vérification de l'index du tableau. Une optimisation importante lorsque vous travaillez avec des tableaux (toutes les classes de collection .NET utilisent un tableau en interne). Lorsque le compilateur JIT peut vérifier qu'une boucle n'indexe jamais un tableau hors limites, il élimine la vérification d'index. Un gros.
Déroulement de la boucle. 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 branche et améliore les options d'exécution super-scalaire du processeur.
Élimination du code mort. Une instruction comme if (false) {/ ... /} est complètement éliminée. Cela peut se produire en raison d'un pliage et d'une doublure 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 profilage du code si délicat.
Levage de code. Le code à l'intérieur d'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 hissage. Il s'agit cependant d'une optimisation coûteuse en raison de l'analyse de flux de données requise et la gigue ne peut pas se permettre le temps, donc ne soulève que les cas évidents. Forcer les programmeurs .NET à écrire un meilleur code source et à se hisser.
Élimination de la sous-expression commune. 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 d'assistance. Pas besoin de compromettre la lisibilité.
Pliage constant. x = 1 + 2; devient x = 3; Cet exemple simple est détecté tôt par le compilateur, mais se produit au moment JIT lorsque d'autres optimisations rendent cela possible.
Propagation de copie. x = a; y = x; devient y = a; Cela aide l'allocateur de registre à prendre de meilleures décisions. C'est un gros problème dans la gigue x86 car il a peu de registres avec lesquels travailler. Le faire sélectionner les bons est essentiel à la performance.
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 vraiment d'importance que lorsque le code se trouve 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 à l'avance ce qui est critique, il ne peut appliquer que le cadran "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 du code qui s'exécute ailleurs. Lecture d'un fichier, exécution d'une requête dbase, etc. Rendre le travail optimiseur JIT complètement invisible. Cela ne me dérange pas cependant :)
L'optimiseur JIT est un code assez fiable, principalement parce qu'il a été testé des millions de fois. Il est extrêmement rare de rencontrer des problèmes dans la version Release build de votre programme. Cela arrive cependant. Les tremblements x64 et x86 ont tous deux eu des problèmes avec les structures. La gigue x86 a des problèmes de cohérence en virgule flottante, produisant des résultats subtilement différents lorsque les intermédiaires d'un calcul en 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.
la source
LinkedList<T>
non, même si elles ne sont pas utilisées très souvent.volatile
mot clé ne s'applique pas aux variables locales stockées dans un cadre de pile. De la documentation à msdn.microsoft.com/en-us/library/x13ttww7.aspx : "Le mot clé volatile ne peut être appliqué qu'aux champs d'une classe ou d'une structure. Les variables locales ne peuvent pas être déclarées volatiles."Debug
etRelease
construit à cet égard est la case à cocher "optimiser le code" qui est normalement activée pourRelease
mais désactivée pourDebug
. C'est juste pour s'assurer que les lecteurs ne commencent pas à penser qu'il existe des différences "magiques" et invisibles entre les deux configurations de build qui vont au-delà de ce qui se trouve sur la page de propriétés du projet dans Visual Studio.Oui, il existe de nombreuses différences de performances et elles s'appliquent vraiment à tout votre code. Le débogage n'optimise que très peu les performances et le mode de libération est très important;
Seul le code qui repose sur le
DEBUG
constante peut fonctionner différemment avec une version de version. En plus de cela, vous ne devriez voir aucun problème.Un exemple de code cadre qui dépend de la
DEBUG
constante est laDebug.Assert()
méthode, dont l'attribut est[Conditional("DEBUG)"]
défini. Cela signifie que cela dépend également de laDEBUG
constante et que cela n'est pas inclus dans la version.la source
DEBUG
:AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length))
.asp.net
et utilisez le débogage au lieu de publier, certains scripts peuvent être ajoutés sur votre page, tels que:MicrosoftAjax.debug.js
qui a environ 7k lignes.Cela dépend fortement de la nature de votre application. Si votre application est lourde d'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 voudrez peut-être tester si vous pouvez percevoir un décalage notable lors de l'exécution dans la version DEBUG.
Cependant, si vous avez de nombreux calculs lourds en calcul, vous remarquerez des différences (pouvant atteindre 40% comme @Pieter l'a mentionné, bien que cela dépende de la nature des calculs).
Il s'agit essentiellement d'un compromis de conception. Si vous publiez sous la version DEBUG, alors si les utilisateurs rencontrent des problèmes, vous pouvez obtenir un retraçage plus significatif et vous pouvez faire un diagnostic beaucoup plus flexible. En publiant dans la version DEBUG, vous évitez également que l'optimiseur produise des Heisenbugs obscurs .
la source
D'après mon expérience, les applications de taille moyenne ou plus grandes sont sensiblement plus réactives dans une version Release. Faites un essai avec votre application et voyez comment elle se sent.
Une chose qui peut vous mordre avec les versions Release est que le code de construction Debug peut parfois supprimer les conditions de concurrence et d'autres bogues liés au thread. Un code optimisé peut entraîner une réorganisation des instructions et une exécution plus rapide peut exacerber certaines conditions de concurrence.
la source
Vous ne devez jamais publier un build de débogage .NET en production. Il peut contenir du code laid pour prendre en charge Edit-and-Continue ou qui sait quoi d'autre. Pour autant que je sache, cela ne se produit que dans VB et non en C # (remarque: le message d'origine est étiqueté C #) , mais cela devrait encore donner raison de faire une pause quant à ce que Microsoft pense qu'ils sont autorisés à faire avec une version de débogage. En fait, avant .NET 4.0, le code VB perd de la mémoire proportionnellement au nombre d'instances d'objets avec des événements que vous construisez à l'appui de Edit-and-Continue. (Bien que ce problème soit signalé 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 désagréable, créer des objets et les ajouter à une liste statique tout entenant un verrou
WeakReference
) Je ne veux certainement pas de ce type de support de débogage dans un environnement de production!la source
D'après mon expérience, la pire chose qui soit sortie du mode Release, ce sont les obscurs "bugs de publication". Étant donné que 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 sortie ne sont pas présentes en mode débogage
Cela m'est arrivé une ou deux fois où une application console simple fonctionnerait parfaitement bien en mode débogage, mais étant donné la même entrée exacte, une erreur se produirait en mode Release. Ces bogues sont EXTRÊMEMENT difficiles à déboguer (par définition du mode Release, ironiquement).
la source
Je dirais que 1) dépend en grande partie de votre mise en œuvre. Habituellement, la différence n'est pas si énorme. J'ai fait beaucoup de mesures et souvent je ne voyais pas de différence. Si vous utilisez du code non managé, beaucoup de tableaux énormes et des trucs comme ça, la différence de performances est légèrement plus grande, mais pas un monde différent (comme en C ++). 2) Habituellement, dans le code de version, moins d'erreurs sont affichées (tolérance plus élevée), donc un commutateur devrait fonctionner correctement.
la source
la source