97 votes

Quelle est la surcharge réelle de try/catch en C# ?

Je sais donc que try/catch ajoute des frais généraux et n'est donc pas un bon moyen de contrôler le flux de processus, mais d'où vient cette surcharge et quel est son impact réel ?

102voto

Shaun Austin Points 2512

Trois points à souligner ici :

  • Tout d'abord, il n'y a que peu ou pas d'inconvénient en termes de performances à avoir des blocs try-catch dans votre code. Cela ne devrait pas être un facteur à prendre en considération lorsque vous essayez d'éviter de les avoir dans votre application. L'impact sur les performances n'entre en jeu que lorsqu'une exception est levée.

  • Lorsqu'une exception est levée, en plus des opérations de déroulement de la pile, etc. qui ont lieu et que d'autres ont mentionnées, vous devez savoir que tout un tas de choses liées à l'exécution/réflexion se produisent afin de remplir les membres de la classe d'exception tels que l'objet de trace de la pile et les différents membres de type, etc.

  • Je pense que c'est l'une des raisons pour lesquelles le conseil général, si vous voulez rejeter l'exception, est de simplement throw; plutôt que de relancer l'exception ou d'en construire une nouvelle, car dans ces cas-là, toutes les informations de la pile sont récupérées, alors qu'elles sont préservées dans le cas de la simple relance.

41 votes

De même : Lorsque vous relancez une exception par "throw ex", vous perdez la trace de la pile originale et la remplacez par la trace de la pile ACTUELLE, ce qui est rarement souhaité. Si vous lancez simplement "throw", la trace de la pile originale dans l'exception est préservée.

0 votes

@Eddie Or throw new Exception("Wrapping layer’s error", ex);

55voto

Mike Stone Points 21293

Je ne suis pas un expert en implémentations de langage (donc prenez ceci avec un grain de sel), mais je pense que l'un des coûts les plus importants est de dérouler la pile et de la stocker pour la trace de la pile. Je soupçonne que cela ne se produit que lorsque l'exception est levée (mais je n'en sais rien), et si c'est le cas, il s'agirait d'un coût caché de taille raisonnable à chaque fois qu'une exception est levée... ce n'est donc pas comme si vous ne faisiez que sauter d'un endroit à l'autre du code, il se passe beaucoup de choses.

Je ne pense pas que ce soit un problème tant que vous utilisez les exceptions pour un comportement EXCEPTIONNEL (donc pas le chemin typique et attendu dans le programme).

35 votes

Plus précisément : essayer est bon marché, attraper est bon marché, lancer est cher. Si vous évitez d'essayer et d'attraper, le lancer reste cher.

5 votes

Hmmm - le marquage ne fonctionne pas dans les commentaires. Pour essayer à nouveau - les exceptions sont pour les erreurs, pas pour les "comportements exceptionnels" ou les conditions : blogs.msdn.com/kcwalina/archive/2008/07/17/

2 votes

@Windows programmer Stats / source s'il vous plaît ?

22voto

RoadWarrior Points 11588

S'agit-il de la surcharge liée à l'utilisation de try/catch/finally lorsque les exceptions ne sont pas levées, ou de la surcharge liée à l'utilisation des exceptions pour contrôler le flux du processus ? Dans ce dernier cas, c'est un peu comme si vous utilisiez un bâton de dynamite pour allumer la bougie d'anniversaire d'un enfant, et les frais généraux associés relèvent des domaines suivants :

  • Vous pouvez vous attendre à des manques supplémentaires de cache en raison de l'exception lancée accédant à des données résidentes qui ne sont pas normalement dans le cache.
  • Vous pouvez vous attendre à des défauts de page supplémentaires du fait que l'exception lancée accède à du code non résident et à des données qui ne font normalement pas partie de l'ensemble de travail de votre application.

    • Par exemple, le lancement de l'exception nécessitera que le CLR trouve l'emplacement des blocs finally et catch en fonction de l'IP actuel et de l'IP de retour de chaque trame jusqu'à ce que l'exception soit traitée plus le bloc de filtrage.
    • coût de construction supplémentaire et résolution du nom afin de créer les cadres à des fins de diagnostic, y compris la lecture des métadonnées, etc.
    • les deux éléments ci-dessus accèdent généralement à du code et à des données "froids", de sorte que les défauts de page dure sont probables si vous avez une pression de mémoire du tout :

      • le CLR essaie de placer le code et les données qui ne sont pas utilisés fréquemment loin des données qui sont utilisées fréquemment pour améliorer la localité, donc cela joue contre vous parce que vous forcez le froid à être chaud.
      • le coût des défauts de la page dure, s'il y en a, éclipsera tout le reste.
  • Les situations de capture typiques sont souvent profondes, et les effets ci-dessus ont donc tendance à être amplifiés (ce qui augmente la probabilité de défauts de page).

Quant à l'impact réel du coût, il peut varier considérablement en fonction de ce qui se passe dans votre code à ce moment-là. Jon Skeet a un bon résumé ici avec quelques liens utiles. J'ai tendance à être d'accord avec son affirmation selon laquelle si vous arrivez à un point où les exceptions nuisent de manière significative à vos performances, vous avez des problèmes en termes d'utilisation des exceptions au-delà de la simple performance.

6voto

Tobi Points 13586

D'après mon expérience, le plus gros surcoût est lié à l'émission d'une exception et à sa gestion. J'ai travaillé une fois sur un projet où un code similaire au suivant était utilisé pour vérifier si quelqu'un avait le droit de modifier un objet. Cette méthode HasRight() était utilisée partout dans la couche de présentation, et était souvent appelée pour des centaines d'objets.

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

Lorsque la base de données de test s'est remplie de données de test, cela a entraîné un ralentissement très visible lors de l'ouverture de nouveaux formulaires, etc.

Je l'ai donc remanié comme suit, ce qui - selon les mesures rapides et sales effectuées ultérieurement - est environ deux ordres de grandeur plus rapide :

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

En résumé, l'utilisation d'exceptions dans un flux de processus normal est environ deux ordres de grandeur plus lente que l'utilisation d'un flux de processus similaire sans exceptions.

1 votes

Pourquoi voudriez-vous lancer une exception ici ? Vous pourriez gérer le cas où vous n'avez pas les droits sur place.

0 votes

@ThunderGr c'est en fait ce que j'ai changé, le rendant deux ordres de magnitude plus rapide.

3voto

dotmad Points 154

Sans compter que s'il se trouve dans une méthode fréquemment appelée, il peut affecter le comportement général de l'application.
Par exemple, je considère que l'utilisation de Int32.Parse est une mauvaise pratique dans la plupart des cas, car elle lève des exceptions pour quelque chose qui peut être attrapé facilement autrement.

Donc, pour conclure tout ce qui a été écrit ici :
1) Utilisez les blocs try..catch pour attraper les erreurs inattendues - presque aucune perte de performance.
2) N'utilisez pas d'exceptions pour les erreurs acceptées si vous pouvez l'éviter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X