Il est parfois affirmé que C ++ 11/14 peut vous aider à améliorer vos performances même lorsque vous compilez simplement du code C ++ 98. La justification se situe généralement dans le sens de la sémantique de déplacement, car dans certains cas, les constructeurs rvalue sont générés automatiquement ou font désormais partie de la STL. Maintenant, je me demande si ces cas étaient déjà réellement traités par RVO ou des optimisations de compilateur similaires.
Ma question est alors de savoir si vous pourriez me donner un exemple réel d'un morceau de code C ++ 98 qui, sans modification, s'exécute plus rapidement à l'aide d'un compilateur prenant en charge les nouvelles fonctionnalités du langage. Je comprends qu'un compilateur conforme standard n'est pas requis pour faire l'élision de copie et c'est pour cette raison que la sémantique de mouvement peut entraîner de la vitesse, mais j'aimerais voir un cas moins pathologique, si vous voulez.
EDIT: Juste pour être clair, je ne demande pas si les nouveaux compilateurs sont plus rapides que les anciens compilateurs, mais plutôt s'il y a du code par lequel ajouter -std = c ++ 14 à mes drapeaux de compilateur, il fonctionnerait plus rapidement (évitez les copies, mais si vous peut proposer autre chose que déplacer la sémantique, je serais aussi intéressé)
la source
std::move
et du déplacement de constructeurs (ce qui nécessiterait des modifications du code existant). La seule chose vraiment liée à ma question était la phrase "Vous obtenez des avantages immédiats en termes de vitesse simplement en recompilant", qui n'est étayée par aucun exemple (elle mentionne STL sur la même diapositive, comme je l'ai fait dans ma question, mais rien de spécifique) ). Je demandais des exemples. Si je lis mal les diapositives, faites-le moi savoir.Réponses:
Je connais 5 catégories générales où la recompilation d'un compilateur C ++ 03 en C ++ 11 peut entraîner des augmentations de performances illimitées qui ne sont pratiquement pas liées à la qualité de l'implémentation. Ce sont toutes des variantes de la sémantique des mouvements.
std::vector
réaffecterchaque fois que le
foo
est réaffecté tampon de 03 en C ++ , il copié tousvector
enbar
.En C ++ 11, il déplace à la place le
bar::data
s, qui est fondamentalement gratuit.Dans ce cas, cela repose sur des optimisations à l'intérieur du
std
conteneurvector
. Dans tous les cas ci-dessous, l'utilisation destd
conteneurs est simplement parce que ce sont des objets C ++ qui ont unemove
sémantique efficace en C ++ 11 "automatiquement" lorsque vous mettez à niveau votre compilateur. Les objets qui ne le bloquent pas et qui contiennent unstd
conteneur héritent également desmove
constructeurs améliorés automatiques .Échec NRVO
Lorsque NRVO (nommé l'optimisation de la valeur de retour) échoue, en C ++ 03, il retombe sur copie, sur C ++ 11, il retombe en mouvement. Les échecs de NRVO sont faciles:
ou même:
Nous avons trois valeurs - la valeur de retour et deux valeurs différentes dans la fonction. Elision permet de «fusionner» les valeurs de la fonction avec la valeur de retour, mais pas entre elles. Ils ne peuvent pas tous les deux être fusionnés avec la valeur de retour sans fusionner les uns avec les autres.
Le problème de base est que l'élision NRVO est fragile, et le code avec des modifications non proches du
return
site peut soudainement avoir des réductions de performances massives à cet endroit sans aucun diagnostic émis. Dans la plupart des cas d'échec NRVO, C ++ 11 se termine par unmove
, tandis que C ++ 03 se termine par une copie.Renvoyer un argument de fonction
L'élision est également impossible ici:
en C ++ 11 c'est pas cher: en C ++ 03 il n'y a aucun moyen d'éviter la copie. Les arguments des fonctions ne peuvent pas être élidés avec la valeur de retour, car la durée de vie et l'emplacement du paramètre et de la valeur de retour sont gérés par le code appelant.
Cependant, C ++ 11 peut passer de l'un à l'autre. (Dans un exemple moins jouet, quelque chose pourrait être fait pour le
set
).push_back
ouinsert
Enfin, l'élision dans les conteneurs ne se produit pas, mais les surcharges C ++ 11 rvalue déplacent les opérateurs d'insertion, ce qui économise des copies.
en C ++ 03 un temporaire
whatever
est créé, puis il est copié dans le vecteurv
. 2std::string
tampons sont alloués, chacun avec des données identiques, et un est rejeté.En C ++ 11, un temporaire
whatever
est créé. Lawhatever&&
push_back
surcharge est alorsmove
temporaire dans le vecteurv
. Unstd::string
tampon est alloué et déplacé dans le vecteur. Un videstd::string
est jeté.Affectation
Volé de la réponse de @ Jarod42 ci-dessous.
L'élision ne peut pas se produire avec l'affectation, mais le déplacement peut.
some_function
renvoie ici un candidat à élider, mais comme il n'est pas utilisé pour construire directement un objet, il ne peut pas être élidé. En C ++ 03, les résultats ci-dessus entraînent la copie du contenu du temporairesome_value
. En C ++ 11, il est déplacé verssome_value
, qui est fondamentalement gratuit.Pour le plein effet de ce qui précède, vous avez besoin d'un compilateur qui synthétise les constructeurs de mouvements et les affectations pour vous.
MSVC 2013 implémente les constructeurs de déplacement dans des
std
conteneurs, mais ne synthétise pas les constructeurs de déplacement sur vos types.Les types contenant
std::vector
s et similaires n'obtiennent donc pas de telles améliorations dans MSVC2013, mais commenceront à les obtenir dans MSVC2015.clang et gcc ont depuis longtemps implémenté des constructeurs de mouvements implicites. Le compilateur d'Intel 2013 prendra en charge la génération implicite de constructeurs de déplacement si vous réussissez
-Qoption,cpp,--gen_move_operations
(ils ne le font pas par défaut dans un effort de compatibilité croisée avec MSVC2013).la source
std
conteneurs de bibliothèque seront tous mis à jour avec lesmove
constructeurs "gratuitement" et (si vous ne les avez pas bloqués) les constructions qui utilisent lesdits objets ( et lesdits objets) commenceront à obtenir une construction de mouvement libre dans un certain nombre de situations. Beaucoup de ces situations sont couvertes par élision en C ++ 03: pas toutes.std
conteneurs ci-dessus est principalement parce qu'ils sont peu coûteux à déplacer exoensivement pour copier le type que vous obtenez «gratuitement» en C ++ 11 lors de la recompilation de C ++ 03. C'estvector::resize
une exception: il utilisemove
en C ++ 11.si vous avez quelque chose comme:
Vous avez obtenu une copie en C ++ 03, tandis que vous avez obtenu une affectation de déplacement en C ++ 11. vous avez donc une optimisation gratuite dans ce cas.
la source
foo().swap(v);