116 votes

Les exceptions en C++ sont-elles vraiment lentes ?

Je regardais Gestion systématique des erreurs en C++-Andrei Alexandrescu Il affirme que les exceptions en C++ sont très très lentes.

Est-ce toujours le cas pour C++98 ?

192voto

Matthieu M. Points 101624

Le principal modèle utilisé aujourd'hui pour les exceptions (Itanium ABI, VC++ 64 bits) est le modèle d'exceptions à coût zéro.

L'idée est qu'au lieu de perdre du temps à mettre en place une garde et à vérifier explicitement la présence d'exceptions partout, le compilateur génère une table latérale qui fait correspondre tout point susceptible de lever une exception (Program Counter) à une liste de gestionnaires. Lorsqu'une exception est levée, cette liste est consultée pour choisir le bon gestionnaire (le cas échéant) et la pile est déroulée.

Par rapport aux if (error) stratégie :

  • le modèle à coût zéro, comme son nom l'indique, est gratuit lorsqu'aucune exception ne se produit
  • il coûte environ 10x/20x un if lorsqu'une exception se produit

Le coût, cependant, n'est pas trivial à mesurer :

  • La table d'appoint est généralement froid et, par conséquent, le récupérer en mémoire prend beaucoup de temps.
  • Déterminer le bon gestionnaire implique l'utilisation de RTTI : de nombreux descripteurs RTTI à récupérer, éparpillés dans la mémoire, et des opérations complexes à exécuter (essentiellement une dynamic_cast test pour chaque gestionnaire)

Il s'agit donc principalement de ratés de cache, ce qui n'est pas négligeable par rapport à du code purement CPU.

Note : pour plus de détails, lisez le Rapport TR18015, chapitre 5.4 Traitement des exceptions (pdf)

Donc, oui, les exceptions sont lentes sur le chemin exceptionnel mais ils sont autrement plus rapides que les contrôles explicites ( if ) en général.

Note : Andrei Alexandrescu semble remettre en question ce "plus rapide". J'ai personnellement vu les choses aller dans les deux sens, certains programmes étant plus rapides avec des exceptions et d'autres étant plus rapides avec des branches, donc il semble effectivement y avoir une perte d'optimisabilité dans certaines conditions.


Est-ce important ?

Je dirais que ce n'est pas le cas. Un programme doit être écrit avec lisibilité à l'esprit, et non les performances (du moins, pas comme premier critère). Les exceptions doivent être utilisées lorsque l'on s'attend à ce que l'appelant ne puisse ou ne veuille pas gérer l'échec sur place, et le faire remonter dans la pile. Bonus : en C++11, les exceptions peuvent être mises en commun entre les threads à l'aide de la bibliothèque standard.

C'est subtil cependant, je prétends que map::find ne devrait pas jeter mais je suis d'accord avec map::find renvoyant un checked_ptr qui se lance si une tentative de déréférencement échoue parce qu'elle est nulle : dans ce dernier cas, comme dans celui de la classe introduite par Alexandrescu, l'appelant choisit entre la vérification explicite et le recours aux exceptions. Responsabiliser l'appelant sans lui donner plus de responsabilités est généralement un signe de bonne conception.

19voto

Arash Points 1761

Vous ne pouvez jamais prétendre à des performances si vous ne convertissez pas le code en assembleur ou si vous ne l'évaluez pas.

Voici ce que vous voyez : (quick-bench)

Le code d'erreur n'est pas sensible au pourcentage d'occurrence. Les exceptions ont un léger surcoût tant qu'elles ne sont pas lancées. Dès que vous les lancez, la misère commence. Dans cet exemple, elles sont lancées dans 0 %, 1 %, 10 %, 50 % et 90 % des cas. Lorsque les exceptions sont levées 90% du temps, le code est 8 fois plus lent que dans le cas où les exceptions sont levées 10% du temps. Comme vous le voyez, les exceptions sont vraiment lentes. Ne les utilisez pas si elles sont lancées fréquemment. Si votre application n'a pas d'exigence de temps réel, n'hésitez pas à les lancer si elles se produisent très rarement.

On voit beaucoup d'avis contradictoires à leur sujet. Mais finalement, les exceptions sont-elles lentes ? Je ne juge pas. Il suffit de regarder le benchmark.

C++ exceptions performance benchmark

13voto

Philipp Points 22441

Cela dépend du compilateur.

GCC, par exemple, était connu pour avoir de très mauvaises performances lors de la gestion des exceptions, mais cela s'est considérablement amélioré au cours des dernières années.

Mais notez que le traitement des exceptions doit - comme son nom l'indique - être l'exception plutôt que la règle dans la conception de votre logiciel. Si vous avez une application qui lève tellement d'exceptions par seconde que cela a un impact sur les performances et que cela est encore considéré comme un fonctionnement normal, alors vous devriez plutôt penser à faire les choses différemment.

Les exceptions sont un excellent moyen de rendre le code plus lisible en éliminant tout le code de gestion des erreurs, mais dès qu'elles font partie du flux normal du programme, elles deviennent vraiment difficiles à suivre. Rappelez-vous qu'une throw est en fait un goto catch déguisé.

3voto

NoSenseEtAl Points 2342

Oui, mais ça n'a pas d'importance. Pourquoi ?
Lisez ceci :
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

En gros, cela signifie que l'utilisation d'exceptions comme celles décrites par Alexandrescu (ralentissement de 50x parce qu'ils utilisent des catch como else ) est tout simplement faux. Ceci étant dit, pour les gens qui aiment faire comme ça Je souhaite que C++22 :) ajoute quelque chose comme :
(à noter qu'il devrait s'agir d'un langage de base puisqu'il s'agit essentiellement d'un compilateur générant du code à partir d'un langage existant).

result = attempt<lexical_cast<int>>("12345");  //lexical_cast is boost function, 'attempt'
//... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)...     
//... By default std::exception is replaced, ofc precise configuration is possible
if (result)
{
     int x = result.get(); // or result.result;
}
else 
{
     // even possible to see what is the exception that would have happened in original function
     switch (result.exception_type())
     //...

}

P.S. Notez également que même si les exceptions sont si lentes... ce n'est pas un problème si vous ne passez pas beaucoup de temps dans cette partie du code pendant l'exécution... Par exemple, si la division de flottants est lente et que vous la rendez 4x plus rapide, cela n'a pas d'importance si vous passez 0,3% de votre temps à faire la division FP...

-2voto

Chris McCabe Points 621

Comme in silico l'a dit, cela dépend de l'implémentation, mais en général, les exceptions sont considérées comme lentes pour toute implémentation et ne devraient pas être utilisées dans du code à haute performance.

EDIT : Je ne dis pas qu'il ne faut pas les utiliser du tout, mais pour le code à haute performance, il est préférable de les é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