Je suis nouveau dans les conversions de style C++ et je crains que l'utilisation de conversions de style C++ ruine les performances de mon application car j'ai un réel -délai critique dans ma routine de service d'interruption.
J'ai entendu dire que certains lancers lèveraient même des exceptions!
Je voudrais utiliser les transtypages de style C++ car cela rendrait mon code plus "robuste". Cependant, s'il y a un impact sur les performances , je n'utiliserai probablement pas de transtypages de style C++ et passerai plutôt plus de temps à tester le code qui utilise des transtypages de style C .
Quelqu'un a-t-il effectué des tests/profils rigoureux pour comparer les performances des modèles de style C++ aux modèles de style C?
Quels ont été vos résultats?
Quelles conclusions avez-vous tirées?
Si la distribution de style C++ peut être remplacée conceptuellement par une distribution de style C, il n'y aura pas de surcharge. Si ce n'est pas le cas, comme dans le cas de dynamic_cast
, pour lequel il n'y a pas d'équivalent C, vous devez payer le coût d'une manière ou d'une autre.
Par exemple, le code suivant:
int x;
float f = 123.456;
x = (int) f;
x = static_cast<int>(f);
génère un code identique pour les deux transtypages avec VC++ - le code est:
00401041 fld dword ptr [ebp-8]
00401044 call __ftol (0040110c)
00401049 mov dword ptr [ebp-4],eax
La seule conversion C++ qui peut lancer est dynamic_cast
lors de la conversion en référence. Pour éviter cela, effectuez un cast vers un pointeur, qui retournera 0 si le cast échoue.
Le seul avec un coût supplémentaire à l'exécution est dynamic_cast
, qui a des capacités qui ne peuvent pas être reproduites directement avec un style C de toute façon. Vous n'avez donc aucun problème.
Le moyen le plus simple de vous en assurer est de demander à votre compilateur de générer la sortie de l'assembleur et d'examiner le code qu'il génère. Par exemple, dans tout compilateur correctement implémenté, reinterpret_cast
disparaîtra complètement, car cela signifie simplement "allez-y aveuglément et prétendez que les données sont de ce type".
Pourquoi y aurait-il un impact sur les performances? Ils effectuent exactement la même fonctionnalité que les lancers C. La seule différence est qu'ils détectent plus d'erreurs au moment de la compilation et sont plus faciles à rechercher dans votre code source.
static_cast<float>(3)
est exactement équivalent à (float)3
, et générera exactement le même code.
Étant donné un float f = 42.0f
reinterpret_cast<int*>(&f)
est exactement équivalent à (int*)&f
, Et générera exactement le même code.
Etc. La seule distribution qui diffère est dynamic_cast
, Qui, oui, peut lever une exception. Mais c'est parce qu'il fait des choses que la distribution de style C ne peut pas faire. N'utilisez donc pas dynamic_cast
À moins d'avoir besoin de ses fonctionnalités.
Il est généralement sûr de supposer que les rédacteurs du compilateur sont intelligents. Étant donné deux expressions différentes qui ont la même sémantique selon la norme, il est généralement sûr de supposer qu'elles seront implémentées de manière identique dans le compilateur.
Oups: Le deuxième exemple devrait être reinterpret_cast, pas dynamic_cast, bien sûr. Fixé maintenant.
Ok, juste pour être absolument clair, voici ce que dit la norme C++:
§5.4.5:
Les conversions effectuées par
- a
const_cast
(5.2.11)- a
static_cast
(5.2.9)- un
static_cast
suivi d'unconst_cast
- a
reinterpret_cast
(5.2.10), ou- un
reinterpret_cast
suivi d'unconst_cast
.peut être effectuée en utilisant la notation cast de conversion de type explicite. Les mêmes restrictions et comportements sémantiques s'appliquent. Si une conversion peut être interprétée de plusieurs manières répertoriées ci-dessus, l'interprétation qui apparaît en premier dans la liste est utilisée, même si une distribution résultant de cette interprétation est mal formée.
Donc, si quelque chose , puisque la conversion de style C est implémentée en termes de transtypages C++, les transtypages de style C devraient être plus lent . (bien sûr qu'ils ne le sont pas, car le compilateur génère le même code dans tous les cas, mais c'est plus plausible que les conversions de style C++ étant plus lentes.)
Il existe quatre modèles de style C++:
const_cast
static_cast
reinterpret_cast
dynamic_cast
Comme déjà mentionné, les trois premiers sont des opérations au moment de la compilation. Il n'y a aucune pénalité d'exécution pour les utiliser. Ce sont des messages adressés au compilateur indiquant que les données déclarées dans un sens doivent être accessibles d'une manière différente. "J'ai dit qu'il s'agissait d'un int*
, Mais permettez-moi d'y accéder comme s'il s'agissait d'un char*
Pointant vers sizeof(int) char
s" ou "J'ai dit que ces données étaient en lecture seule, et maintenant je dois le passer à une fonction qui ne le modifiera pas, mais ne prend pas le paramètre comme référence const. "
Mis à part la corruption des données en transtypant vers le mauvais type et en rétrogradant les données (toujours une possibilité avec les transtypages de style C), le problème d'exécution le plus courant avec ces transtypages est les données qui sont réellement déclarées const
peut ne pas être transtypable à non const. Le transtypage d'un élément déclaré const
en non-const puis sa modification n'est pas défini. ndefined signifie que vous n'êtes même pas assuré d'obtenir un crash .
dynamic_cast
Est une construction d'exécution et doit avoir un coût d'exécution.
La valeur de ces castings est qu'ils disent précisément ce que vous essayez de lancer de/vers, ressortent visuellement et peuvent être recherchés avec des outils cérébraux. Je recommanderais de les utiliser plutôt que d'utiliser des modèles de style C.
Lors de l'utilisation de dynamic_cast
plusieurs vérifications sont effectuées pendant l'exécution pour vous empêcher de faire quelque chose de stupide (plus sur la liste de diffusion GCC ), le coût d'une dynamic_cast
dépend du nombre de classes concernées, des classes concernées, etc.
Si vous êtes vraiment sûr que la distribution est sûre, vous pouvez toujours utiliser reinterpret_cast
.
Bien que je sois d'accord avec l'énoncé "le seul avec un coût supplémentaire à l'exécution est dynamic_cast
", gardez à l'esprit qu'il peut y avoir des différences spécifiques au compilateur.
J'ai vu quelques bugs déposés contre mon compilateur actuel où la génération ou l'optimisation de code était légèrement différente selon que vous utilisiez un style C vs C++ - style static_cast
cast.
Donc, si vous êtes inquiet, vérifiez le démontage sur les hotspots. Sinon, évitez simplement les lancements dynamiques lorsque vous n'en avez pas besoin. (Si vous désactivez RTTI, vous ne pouvez pas utiliser dynamic_cast
en tous cas.)
La vérité canonique est l'Assemblée, alors essayez les deux et voyez si vous obtenez une logique différente.
Si vous obtenez exactement le même assemblage, il n'y a pas de différence - il ne peut pas y en avoir. Le seul endroit où vous devez vraiment vous en tenir aux anciennes conversions en C est dans les routines et les bibliothèques en C pur, où il est inutile d'introduire une dépendance C++ uniquement pour la conversion de type.
Une chose à savoir est que les conversions se produisent partout dans un morceau de code de taille décente. Dans toute ma carrière, je n'ai jamais cherché sur "tous les lancers" dans un morceau de logique - vous avez tendance à rechercher des lancers vers un TYPE spécifique comme 'A', et une recherche sur "(A)" est généralement aussi efficace que quelque chose comme "static_cast <A>". Utilisez les modèles plus récents pour des choses comme la validation de type et autres, non pas parce qu'ils facilitent les recherches de toute façon.