Je ne veux pas de discussion sur quand et ne pas lancer d'exceptions. Je souhaite résoudre un problème simple. 99% du temps, l'argument pour ne pas lancer d'exceptions tourne autour de leur lenteur, tandis que l'autre partie affirme (avec un test de référence) que la vitesse n'est pas le problème. J'ai lu de nombreux blogs, articles et articles d'un côté ou de l'autre. Alors qu'est-ce que c'est?
c#
.net
performance
exception
Goran
la source
la source
Réponses:
Je suis du côté "pas lent" - ou plus précisément "pas assez lent pour que cela vaille la peine de les éviter en utilisation normale". J'ai écrit deux courts articles à ce sujet. Il y a des critiques sur l'aspect de référence, qui sont principalement dues au fait que "dans la vraie vie, il y aurait plus de pile à parcourir, donc vous feriez sauter le cache, etc." - mais utiliser des codes d'erreur pour gravir la pile serait également faire sauter le cache, donc je ne vois pas cela comme un argument particulièrement bon.
Pour être clair, je ne soutiens pas l'utilisation d'exceptions là où elles ne sont pas logiques. Par exemple,
int.TryParse
est tout à fait approprié pour convertir les données d'un utilisateur. C'est approprié lors de la lecture d'un fichier généré par la machine, où l'échec signifie «Le fichier n'est pas dans le format qu'il est censé être, je ne veux vraiment pas essayer de gérer cela car je ne sais pas ce qui pourrait être faux. "Lorsque j'utilise des exceptions dans «des circonstances raisonnables», je n'ai jamais vu une application dont les performances étaient considérablement affectées par des exceptions. Fondamentalement, les exceptions ne devraient pas se produire souvent, sauf si vous avez des problèmes d'exactitude importants, et si vous avez des problèmes d'exactitude importants, les performances ne sont pas le plus gros problème auquel vous êtes confronté.
la source
Il y a la réponse définitive à cela de la part du gars qui les a mis en œuvre - Chris Brumme. Il a écrit un excellent article de blog sur le sujet (avertissement - c'est très long) (avertissement2 - c'est très bien écrit, si vous êtes un technicien, vous le lirez jusqu'à la fin et vous devrez ensuite rattraper vos heures après le travail :) )
Le résumé: ils sont lents. Ils sont implémentés en tant qu'exceptions Win32 SEH, donc certains passeront même la limite du processeur ring 0! De toute évidence, dans le monde réel, vous ferez beaucoup d'autres travaux, de sorte que l'exception étrange ne sera pas du tout remarquée, mais si vous les utilisez pour le flux du programme, attendez-vous à ce que votre application soit martelée. Ceci est un autre exemple de la machine marketing MS qui nous rend un mauvais service. Je me souviens d'un microsoftie nous disant comment ils ont encouru des frais généraux absolument nuls, ce qui est complet.
Chris donne une citation pertinente:
la source
Je n'ai aucune idée de ce dont les gens parlent lorsqu'ils disent qu'ils sont lents seulement s'ils sont jetés.
EDIT: Si les exceptions ne sont pas lancées, cela signifie que vous faites une nouvelle exception () ou quelque chose du genre. Sinon, l'exception entraînera la suspension du thread et le parcours de la pile. Cela peut être correct dans des situations plus petites, mais dans les sites Web à fort trafic, le fait de s'appuyer sur des exceptions comme mécanisme de flux de travail ou de chemin d'exécution vous posera certainement des problèmes de performances. Les exceptions, en soi, ne sont pas mauvaises et sont utiles pour exprimer des conditions exceptionnelles
Le flux de travail d'exception dans une application .NET utilise des exceptions de première et de seconde chance. Pour toutes les exceptions, même si vous les attrapez et les gérez, l'objet d'exception est toujours créé et l'infrastructure doit toujours parcourir la pile pour rechercher un gestionnaire. Si vous attrapez et relancez, bien sûr, cela prendra plus de temps - vous allez obtenir une exception de première chance, l'attraper, la renvoyer, provoquant une autre exception de première chance, qui ne trouve alors pas de gestionnaire, ce qui provoque alors une exception de la seconde chance.
Les exceptions sont également des objets sur le tas - donc si vous lancez des tonnes d'exceptions, vous causez à la fois des problèmes de performances et de mémoire.
De plus, selon ma copie de "Performance Testing Microsoft .NET Web Applications" écrite par l'équipe ACE:
"La gestion des exceptions est coûteuse. L'exécution du thread impliqué est suspendue pendant que CLR se répète dans la pile d'appels à la recherche du bon gestionnaire d'exceptions, et lorsqu'il est trouvé, le gestionnaire d'exceptions et un certain nombre de blocs finally doivent tous avoir une chance de s'exécuter. avant que le traitement normal puisse être effectué. "
Ma propre expérience sur le terrain a montré que la réduction des exceptions améliorait considérablement les performances. Bien sûr, il y a d'autres choses que vous prenez en compte lors des tests de performances - par exemple, si vos E / S disque sont prises ou si vos requêtes sont dans les secondes, cela devrait être votre objectif. Mais trouver et supprimer des exceptions devrait être un élément essentiel de cette stratégie.
la source
L'argument tel que je le comprends n'est pas que le fait de lancer des exceptions est mauvais, mais lent en soi. Au lieu de cela, il s'agit d'utiliser la construction throw / catch comme un moyen de première classe de contrôler la logique d'application normale, au lieu de constructions conditionnelles plus traditionnelles.
Souvent, dans la logique d'application normale, vous effectuez une boucle où la même action est répétée des milliers / millions de fois. Dans ce cas, avec un profilage très simple (voir la classe Stopwatch), vous pouvez voir par vous-même que lancer une exception au lieu de dire une simple instruction if peut s'avérer considérablement plus lent.
En fait, j'ai lu une fois que l'équipe .NET de Microsoft avait introduit les méthodes TryXXXXX dans .NET 2.0 pour de nombreux types de FCL de base spécifiquement parce que les clients se plaignaient que les performances de leurs applications étaient si lentes.
Il s'avère que dans de nombreux cas, cela était dû au fait que les clients tentaient de convertir le type de valeurs dans une boucle et que chaque tentative échouait. Une exception de conversion a été levée puis interceptée par un gestionnaire d'exceptions qui a ensuite avalé l'exception et continué la boucle.
Microsoft recommande maintenant que les méthodes TryXXX soient utilisées en particulier dans cette situation pour éviter de tels problèmes de performances possibles.
Je peux me tromper, mais il semble que vous ne soyez pas certain de la véracité des «benchmarks» que vous avez lus. Solution simple: essayez-le par vous-même.
la source
Mon serveur XMPP a considérablement augmenté sa vitesse (désolé, pas de chiffres réels, purement observationnel) après avoir constamment essayé de les empêcher de se produire (comme vérifier si une prise est connectée avant d'essayer de lire plus de données) et me donner des moyens de les éviter (les méthodes TryX mentionnées). C'était avec seulement environ 50 utilisateurs virtuels actifs (en conversation).
la source
Juste pour ajouter ma propre expérience récente à cette discussion: conformément à la plupart de ce qui est écrit ci-dessus, j'ai trouvé que le lancement d'exceptions était extrêmement lent lorsqu'il était répété, même sans le débogueur en cours d'exécution. J'ai simplement augmenté les performances d'un grand programme que j'écris de 60% en modifiant environ cinq lignes de code: passer à un modèle de code de retour au lieu de lancer des exceptions. Certes, le code en question s'exécutait des milliers de fois et lançait potentiellement des milliers d'exceptions avant que je ne le modifie. Donc, je suis d'accord avec la déclaration ci-dessus: lancer des exceptions lorsque quelque chose d'important tourne mal, et non comme un moyen de contrôler le flux d'application dans des situations "attendues".
la source
Si vous les comparez aux codes de retour, ils sont lents comme l'enfer. Cependant, comme les affiches précédentes l'ont déclaré, vous ne voulez pas lancer le fonctionnement normal du programme, vous n'obtenez donc le coup de performance que lorsqu'un problème survient et dans la grande majorité des cas, les performances n'ont plus d'importance (car l'exception implique de toute façon un barrage routier).
Ils valent vraiment la peine d'être utilisés sur les codes d'erreur, les avantages sont vastes de l'OMI.
la source
Je n'ai jamais eu de problème de performances avec des exceptions. J'utilise beaucoup les exceptions - je n'utilise jamais de codes de retour si je le peux. Ils sont une mauvaise pratique et, à mon avis, ils sentent le code de spaghetti.
Je pense que tout se résume à la façon dont vous utilisez les exceptions: si vous les utilisez comme des codes de retour (chaque appel de méthode dans la pile capture et relance) alors, oui, ils seront lents, car vous avez une surcharge à chaque capture / lancer.
Mais si vous lancez au bas de la pile et attrapez en haut (vous remplacez toute une chaîne de codes de retour par un lancer / attraper), toutes les opérations coûteuses sont effectuées une fois.
En fin de compte, ils constituent une fonctionnalité linguistique valide.
Juste pour prouver mon point
Veuillez exécuter le code sur ce lien (trop gros pour une réponse).
Résultats sur mon ordinateur:
marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM
Les horodatages sont affichés au début, entre les codes de retour et les exceptions, à la fin. Cela prend le même temps dans les deux cas. Notez que vous devez compiler avec des optimisations.
la source
Mais mono lève l'exception 10 fois plus vite que le mode autonome .net, et le mode autonome .net lance l'exception 60 fois plus vite que le mode débogueur .net. (Les machines de test ont le même modèle de CPU)
la source
En mode de libération, la surcharge est minime.
À moins que vous n'utilisiez des exceptions pour le contrôle de flux (par exemple, des sorties non locales) de manière récursive, je doute que vous puissiez remarquer la différence.
la source
Sur le CLR Windows, pour une chaîne d'appels de profondeur 8, lever une exception est 750 fois plus lent que la vérification et la propagation d'une valeur de retour. (voir ci-dessous pour les benchmarks)
Ce coût élevé des exceptions est dû au fait que Windows CLR s'intègre à quelque chose appelé Gestion des exceptions structurée Windows . Cela permet aux exceptions d'être correctement interceptées et lancées dans différents environnements d'exécution et langages. Cependant, c'est très très lent.
Les exceptions dans l'environnement d'exécution Mono (sur n'importe quelle plate-forme) sont beaucoup plus rapides, car elles ne s'intègrent pas à SEH. Cependant, il y a une perte de fonctionnalité lors du passage d'exceptions sur plusieurs runtimes car il n'utilise rien comme SEH.
Voici les résultats abrégés de mon benchmark des exceptions par rapport aux valeurs de retour pour le CLR Windows.
Et voici le code ..
la source
Une note rapide ici sur les performances associées à la capture des exceptions.
Lorsque le chemin d'exécution entre dans un bloc 'try', rien de magique ne se produit. Il n'y a pas d'instruction «try» et aucun coût associé à l'entrée ou à la sortie du bloc try. Les informations sur le bloc try sont stockées dans les métadonnées de la méthode, et ces métadonnées sont utilisées au moment de l'exécution chaque fois qu'une exception est déclenchée. Le moteur d'exécution parcourt la pile à la recherche du premier appel contenu dans un bloc try. Toute surcharge associée à la gestion des exceptions se produit uniquement lorsque des exceptions sont levées.
la source
Lors de l'écriture de classes / fonctions que d'autres peuvent utiliser, il semble difficile de dire quand les exceptions sont appropriées. Il y a quelques parties utiles de BCL que j'ai dû abandonner et opter pour pinvoke car elles lancent des exceptions au lieu de renvoyer des erreurs. Dans certains cas, vous pouvez contourner ce problème, mais pour d'autres comme System.Management et Performance Counters, il existe des utilisations dans lesquelles vous devez effectuer des boucles dans lesquelles des exceptions sont fréquemment levées par BCL.
Si vous écrivez une bibliothèque et qu'il y a une faible possibilité que votre fonction soit utilisée dans une boucle et qu'il y ait un potentiel pour un grand nombre d'itérations, utilisez le modèle Try .. ou une autre façon d'exposer les erreurs à côté des exceptions. Et même dans ce cas, il est difficile de dire à quel point votre fonction sera appelée si elle est utilisée par de nombreux processus dans un environnement partagé.
Dans mon propre code, les exceptions ne sont lancées que lorsque les choses sont si exceptionnelles qu'il est nécessaire d'aller regarder la trace de la pile et de voir ce qui n'a pas fonctionné, puis de le réparer. J'ai donc pratiquement réécrit des parties de BCL pour utiliser la gestion des erreurs basée sur le modèle Try .. au lieu d'exceptions.
la source