Il est toujours plus sûr (de justesse) pour laisser votre appel de programme operator delete
avec un nullptr.
Pour les performances, il est très rare que le fait d'avoir généré par le compilateur asm fait faire un test et de la branche conditionnelle pour passer d'un appel à l' operator delete
sera une victoire. (Vous pouvez vous aider de gcc optimiser loin au moment de la compilation nullptr
suppression sans l'ajout d'un moment de l'exécution, bien que; voir ci-dessous).
Tout d'abord, plus de code de taille en dehors d'un vrai hot-spot augmente la pression sur le L1I cache, et même la plus petite décodé-uop cache sur les Processeurs x86 qui ont un (Intel banque nationale, de la famille, AMD Ryzen).
Deuxièmement, extra branches conditionnelles utilisation des entrées dans la branche de la prédiction des caches (BTB = Direction de la Cible Tampon et ainsi de suite). Selon le CPU, même une branche qui n'a jamais pris le peut aggraver les prédictions pour les autres branches si c'alias dans le BTB. (Sur les autres, telle une branche n'est jamais une entrée dans le BTB, pour enregistrer les entrées pour les branches où la statique par défaut de prévision de l'automne est exacte.) Voir https://xania.org/201602/bpu-part-one.
Si nullptr
est rare dans un chemin de code, puis sur la moyenne de la vérification et de la direction générale pour éviter l' call
se termine avec votre programme de passer plus de temps sur la coche de la case à sauve.
Si le profilage montre que vous avez un hot-spot qui comprend un delete
, et de l'instrumentation / enregistrement montre qu'elle a souvent fait des appels delete
avec un nullptr, alors il vaut la peine d'essayer
if (ptr) delete ptr;
au lieu de simplement en delete ptr;
Direction de la prévision pourrait avoir plus de chance qu'un site d'appel que, pour la direction, à l'intérieur d' operator delete
, surtout si il y a une corrélation avec d'autres à proximité des branches. (Apparemment moderne Mru, ne regardez pas seulement chaque branche dans l'isolation). C'est au sommet d'enregistrement les inconditionnels call
dans la fonction de bibliothèque (avec un jmp
de la PLT relevé, de la liaison dynamique de surcharge sur Unix/Linux).
Si vous êtes à la vérification de la valeur null pour toute autre raison, alors il peut faire sens pour mettre la delete
à l'intérieur de la non-nulle de la branche de votre code.
Vous pouvez éviter delete
des appels dans les cas où gcc peut prouver (après inlining) qu'un pointeur est null, mais sans en faire un moment de l'exécution si ce n':
static inline bool
is_compiletime_null(const void *ptr) {
#ifdef __GNUC__
// __builtin_constant_p(ptr) is false even for nullptr,
// but the checking the result of booleanizing works.
return __builtin_constant_p(!ptr) && !ptr;
#else
return false;
#endif
}
Il renverra toujours false avec clang, car il évalue __builtin_constant_p
avant l'in-lining. Mais depuis clang déjà saute delete
des appels lorsqu'il peut prouver un pointeur est null, vous n'en avez pas besoin.
Cela pourrait effectivement aider en std::move
des cas, et vous pouvez l'utiliser en toute sécurité n'importe où avec (en théorie) pas de performance à la baisse. J'ai toujours compile if(true)
ou if(false)
, il est donc très différente de if(ptr)
, qui est de nature à entraîner l'exécution de la branche, car le compilateur ne peut probablement pas prouver que le pointeur n'est pas null dans la plupart des cas, soit. (Un déréférencement peut, cependant, car un nul deref serait UB, et les compilateurs modernes optimisé basé sur l'hypothèse que le code ne contient aucune UB).
Vous pourriez en faire une macro pour éviter les ballonnements non-optimisé construit (et donc qu'il serait "travail" sans avoir à inline premier). Vous pouvez utiliser un système GNU C déclaration expression afin d'éviter une double évaluation de la macro arg (voir les exemples pour GNU C min()
et max()
). Pour la solution de repli pour les compilateurs sans extensions GNU, vous pouvez écrire ((ptr), false)
ou quelque chose à évaluer l'arg une fois que les effets secondaires, tout en produisant un false
résultat.
Démonstration: asm de gcc6.3 -O3 sur la Godbolt compilateur explorer
void foo(int *ptr) {
if (!is_compiletime_null(ptr))
delete ptr;
}
# compiles to a tailcall of operator delete
jmp operator delete(void*)
void bar() {
foo(nullptr);
}
# optimizes out the delete
rep ret
Il compile correctement avec MSVC (également sur le compilateur explorer le lien), mais avec le test retourne toujours false, bar()
est:
# MSVC doesn't support GNU C extensions, and doesn't skip nullptr deletes itself
mov edx, 4
xor ecx, ecx
jmp ??3@YAXPEAX_K@Z ; operator delete
Intéressant de noter que MSVC de l' operator delete
prend la taille de l'objet en tant que
une fonction arg (mov edx, 4
), mais gcc/Linux/libstdc++ code passe le pointeur.
Connexes: j'ai trouvé ce blog, à l'aide de C11 (pas de C++11) _Generic
pour essayer de portably faire quelque chose comme __builtin_constant_p
de pointeur null contrôles à l'intérieur des initialiseurs statiques.