Comment puis-je être sûr qu’une routine profite du (N)RVO ?
-
09-12-2019 - |
Question
J'aimerais m'assurer que mes routines tirent parti du (N)RVO autant que possible.À part analyser le démontage résultant, y a-t-il quelque chose que je puisse faire ou vérifier si une routine est en cours de compilation avec (N)RVO ?À ce stade, je m'intéresse principalement à MSVC et GCC.
La solution
Non, pas vraiment.
Cependant, vous pouvez suivre les directives lors de l'écriture de votre code.
Optimisation de la valeur de retour sans nom
Ceci est pratiquement déclenché à chaque fois que vous renvoyez un temporaire, même en mode débogage.
return MyObject(....);
Optimisation de la valeur de retour nommée
Ceci est quasiment déclenché à chaque fois que la fonction renvoie toujours le même objet temporaire :
MyObject func() {
MyObject result;
if (...) { return result; }
result.push(0);
return result;
}
Vous pouvez les mélanger, mais il devient quasiment impossible pour le compilateur d'appliquer RVO dans ce cas :
MyObject func() {
MyObject result;
if (...) { return MyObject(...); }
return result;
}
Ici, il est probable qu’un retour bénéficiera du RVO et l’autre non.Et je parierais sur l'optimisation du premier car vous seriez bloqué si vous créiez de manière spéculative result
dans la fente de retour et je dois soudainement prendre le if
bifurquer.Notez que la simple réorganisation des instructions fonctionne :
MyObject func() {
if (...) { return MyObject(...); }
MyObject result;
return result;
}
La règle générale pour le NRVO est donc qu'il ne devrait y avoir aucun return
déclaration entre la déclaration de result
et le return result;
instruction qui renvoie autre chose que result
lui-même.
Si vous suivez cela, vous mettez toutes les chances de votre côté.Et puis c'est juste une question de révision du code.
Et vous rendez également votre code plus facile à lire puisque vous ne déclarez pas de variables avant de savoir que vous en avez vraiment besoin !
Autres conseils
Vous pouvez ajouter des méthodes de débogage au destructeur:
struct A
{
~A() { cout << "destructor called"; }
};
A foo()
{
A a;
return a;
}
Si le destructeur est appelé, le RVO n'était probablement pas appliqué.
Les moyens possibles auxquels je peux penser sont :
Implémenter un mécanisme de comptage de références au sein de votre classe qui garde une trace du nombre d'instances créées via la classe, quelque chose comme un
shared_ptr
le fait, de cette façon, vous pouvez détecter des copies supplémentaires de votre classe en cours de création et de suppression si l'élision de copie ne se produit pas.Vous pouvez simplement mettre des traces de débogage dans le constructeur de copie et le destructeur de votre classe, si l'élision de copie ne se produit pas, vous verrez beaucoup de traces de débogage successives du constructeur de copie et du destructeur.