64 votes

L'utilisation de std::move() est-elle une optimisation prématurée ?

Supposons que j'aie le code suivant :

int main()
{
    std::vector<std::string> strs;

    std::string var("Hello World");
    // Make some modifications to 'var'
    strs.push_back(std::move(var));
}

La partie de l'échantillon que je souhaite souligner est l'utilisation de std::move() . Je m'inquiète essentiellement de l'existence d'une copie sur le site de la push_back() appel. Supposons que la chaîne que j'ajoute soit très grande. Je suis encore en train d'apprendre les références aux valeurs r de C++11, donc je ne suis pas sûr de la façon dont le compilateur optimiserait la copie (s'il le fait) sans l'option std::move() .

Quelqu'un peut-il expliquer s'il s'agit d'une optimisation prématurée (forcer les déplacements dans tous les cas où l'on veut éviter les copies, en général) ? Si c'est le cas, quels sont les modèles que le compilateur devrait suivre (ou qu'il est le plus probable qu'il suive) et qui résulteraient en un déplacement optimisé et automatique ici ?

EDITAR

Je tiens à ajouter que je comprends comment les mouvements automatiques se produisent sur le marché de l'emploi. valeurs de retour des fonctions L'OAV/ROV s'applique. L'exemple spécifique que j'ai donné ici ne s'appliquerait pas à l'OAV, je n'en suis donc pas certain.

28voto

Jonathan Wakely Points 45593

Je ne suis pas sûr que le compilateur puisse optimiser la copie (si tant est qu'il le fasse) sans l'option std::move() .

Seule une très Un compilateur intelligent pourrait optimiser ce point, donc si la copie risque d'être coûteuse (par exemple, une chaîne de caractères très longue), il est préférable de la déplacer.

Sans le mouvement, le code est en fait une suite d'appels à :

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector
malloc  // allocate new string
strcpy  // copy from var to new string
free    // destroy var

Avec le déménagement, il devient :

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector

En théorie, un compilateur intelligent pourrait effectuer cette transformation automatiquement, mais pour que le compilateur puisse voir à travers toutes les couches d'abstraction introduites par les constructeurs et les destructeurs, ainsi que par le système de gestion de l'information, il faut qu'il soit capable de voir à travers les couches d'abstraction. vector est assez difficile, de sorte qu'il est possible de prouver que le code peut être transformé pour supprimer une fonction membre. malloc y free est compliqué.

23voto

Peter Points 1765

Les autres réponses se concentrent trop sur les aspects techniques de la question à mon goût, je vais donc essayer de donner une réponse plus générale.

En bref : Non, utiliser un "truc" comme std::move de la manière décrite dans la question n'est pas une optimisation prématurée. Ne pas utiliser std::move lorsqu'il peut être utilisé est également une bonne chose, à moins que le code ne soit déjà connu pour être critique en termes de performances.

La nécessité d'éviter l'optimisation prématurée est parfois comprise comme "n'optimisez pas, jusqu'à ce que vous puissiez prouver que c'est nécessaire", mais je préfère la lire comme : "Ne perdez pas de temps à résoudre des problèmes si vous ne savez pas qu'ils doivent être résolus.

Les optimisations prématurées nécessitent des efforts pour optimiser ce qui n'a peut-être pas besoin de l'être, et transforment généralement un problème simple en un problème complexe. De ce point de vue, je qualifierais d'optimisation prématurée toute longue réflexion sur la question elle-même.

Un exemple connexe : Les personnes travaillant dans des codes critiques en termes de performance passeront souvent des arguments sous forme de références constantes ( const std::string& ). Parce que c'est ce qu'ils ont l'habitude de faire, ils utiliseront le même modèle dans le code qui n'est pas critique en termes de performances, même s'ils pourraient simplement utiliser la méthode "pass-by-copy" ( const std::string ou même std::string ). Il ne s'agit pas non plus d'une optimisation prématurée.

20voto

CashCow Points 18388

Après std::move l'objet d'origine, en l'occurrence var doit être dans un état valide mais peut contenir n'importe quelle valeur, par exemple il peut être vide.

Si vous savez que vous n'allez pas utiliser var et vous ne l'avez créé que pour le mettre dans le vector alors il ne s'agit pas vraiment d'une optimisation "prématurée" comme le veut l'intention de ce que vous essayez de faire.

vector dispose d'une nouvelle méthode emplace_back() qui est la même chose mais plus claire, et qui utilise des arguments avancés (ici vous faites juste emplace_back("Hello World") si vous ne faites que le construire). Dans votre cas, étant donné que vous "apportez quelques modifications avec var" emplace_back n'est pas susceptible de ne pas être approprié.

Dans l'ancien C++ vous pouvez optimiser la copie en faisant push_back() sur une chaîne vide puis l'échange.

18voto

Paul Points 173

Je ne sais pas pourquoi tout le monde suggère l'utilisation de emplace_back() . L'objectif de la emplace_back() est d'éviter les opérations de copie/déplacement en construisant l'objet sur place. Dans ce cas, vous avez déjà construit l'objet, de sorte qu'au moins une opération de copie/déplacement est inévitable. Il n'y a aucun avantage à utiliser emplace_back() plus push_back() dans ce cas.

Sinon, je suis d'accord avec tous ceux qui disent que ce n'est pas une optimisation prématurée parce que la sémantique du déplacement modélise mieux ce que vous essayez de faire que de faire une copie de l'objet.

1voto

cmaster Points 7460

Oui, il s'agit d'une optimisation prématurée s'il s'agit d'une optimisation prématurée.

Permettez-moi de préciser :
L'optimisation prématurée est définie par le fait que vous optimisez une partie du code dont vous ne savez pas si elle est critique en termes de performances. En d'autres termes, l'optimisation prématurée n'est jamais définie par la méthode d'optimisation, elle est toujours définie par le lieu de l'optimisation et votre connaissance de ce que vous faites.


Passons maintenant à l'opportunité d'optimiser l'utilisation de std::move() :

Avec std::move() vous évitez les tâches lourdes de la construction de copies, comme l'allocation de mémoire, la désallocation, la construction d'éléments contenus par copie, la déconstruction d'éléments contenus, etc. C'est une bonne chose. Mais il reste une partie : la construction/destruction de l'objet conteneur.

emplace_back() a la vertu d'éviter totalement la construction de l'objet temporaire. Ainsi, chaque fois que vous pouvez éviter un objet temporaire en utilisant emplace_back() Vous obtenez ainsi une petite victoire.

Considérer la qualité du code (lisibilité/maintenabilité) : std::move() a l'inconvénient de laisser un objet inutilisable, ce qui peut être une source de bogues. Ce n'est pas le cas avec emplace_back() . Une fois de plus, la seconde solution est donc nettement préférable.

Bien sûr, emplace_back() ne peut pas être utilisé dans tous les contextes qui autorisent l'utilisation de std::move() . Donc, si ces éléments sont essentiels à la performance, std::move() peut être la solution. Il existe des cas d'utilisation très valables pour l'optimisation avec std::move() . Cependant, vous pourriez également découvrir que vous pouvez écrire le code d'une manière qui ne nécessite aucune construction de copie/déplacement/emplacement. Ne cessez jamais de chercher une meilleure solution lorsque vous optimisez !

En fin de compte, il reste que la validité de l'optimisation à l'aide de std::move() dépend entièrement du contexte : C'est une bonne optimisation partout où c'est une bonne optimisation.

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