54 votes

La RVO (Return Value Optimization) est-elle applicable à tous les objets ?

Est-ce que RVO ( Optimisation de la valeur de retour ) garanti ou applicable pour tous les objets et situations dans les compilateurs C++ (spécialement GCC) ?

Si la réponse est "non", quelles sont les conditions de cette optimisation pour une classe/un objet ? Comment puis-je forcer ou encourager le compilateur à faire une RVO sur une valeur retournée spécifique ?

52voto

L'optimisation de la valeur de retour peut toujours être appliqué, ce qui ne peut pas être appliqué universellement est Nommé Optimisation de la valeur de retour. Fondamentalement, pour que l'optimisation ait lieu, le compilateur doit connaître quel objet va être retourné à l'endroit où l'objet est construit.

Dans le cas de RVO (où un objet temporaire est retourné), cette condition est trivialement remplie : l'objet est construit dans l'instruction de retour, et bien, il est retourné.

Dans le cas de NRVO, il faudrait analyser le code pour comprendre si le compilateur peut connaître ou non cette information. Si l'analyse de la fonction est simple, il y a de fortes chances que le compilateur l'optimise (déclaration de retour unique qui ne contient pas de conditionnel, par exemple ; déclarations de retour multiples du même objet ; déclarations de retour multiples comme T f() { if (condition) { T r; return r; } else { T r2; return r2; } } où le compilateur sait que r o r2 sera être renvoyé...)

Notez que vous ne pouvez supposer l'optimisation que dans des cas simples. Plus précisément, l'exemple de wikipedia pourrait en fait être optimisé par un compilateur suffisamment intelligent :

std::string f( bool x ) {
   std::string a("a"), b("b");
   if ( x ) return a; 
   else return b;
}

Peut être réécrit par le compilateur en :

std::string f( bool x ) {
   if ( x ) {
      std::string a("a"), b("b");
      return a;
   } else {
      std::string a("a"), b("b");
      return b;
   }
}

Et le compilateur peut savoir à ce moment-là que dans la première branche a doit être construit à la place de l'objet retourné, et dans la deuxième branche, la même chose s'applique à b . Mais je ne compterais pas là-dessus. Si le code est complexe, supposez que le compilateur ne sera pas en mesure de produire l'optimisation.

EDIT : Il y a un cas que je n'ai pas mentionné explicitement, le compilateur n'est pas autorisé (dans la plupart des cas, même s'il était autorisé, il ne pourrait pas le faire) à optimiser la copie d'un argument de la fonction vers l'instruction de retour :

T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
                                 // a move operation if available.

0 votes

Mieux vaut corriger l'article de wikipedia et les références qu'il cite, y compris un article de Hinnant.

0 votes

L'article de wikipedia n'est pas mauvais le commentaire de l'exemple indique explicitement L'OAV pourrait ne pas être appliqué . Je soulignais simplement que, selon le compilateur, même des copies non triviales pourraient être évitées.

3 votes

Notez que le RVO/NRVO n'est applicable que lors du renvoi d'un objet temporaire c'est-à-dire un objet qui devrait normalement être détruit après la copie. Par exemple, lors du retour d'une variable membre à partir d'une fonction membre, une copie doit avoir lieu. Cela peut sembler évident pour certains, mais j'ai dû l'expliquer à de nombreuses personnes qui venaient de découvrir le RVO.

5voto

La RVO (Return Value Optimization) est-elle garantie pour tous les objets dans les compilateurs gcc ?

Aucune optimisation n'est jamais garanti (bien que le RVO soit assez fiable, il existe certains cas qui le déstabilisent ).

Si la réponse est "non", quelles sont les conditions de cette optimisation pour une classe/un objet ?

Un détail d'implémentation qui est délibérément abstrait de vous.

Ni savoir ni se soucier de cela, s'il vous plaît.

7 votes

Votre dernier point est un commentaire un peu Javaish.. ;) Il n'y a pas de mal à savoir, cela peut être spécifique à l'implémentation, mais si cela vous tente, pourquoi pas ? Cependant, pour ce qui est de s'en soucier, seulement si le profilage le met en évidence...

1 votes

@Nim : C'est "mal" de vouloir savoir quelles optimisations sont effectuées. OK, d'accord, si vous êtes vraiment en train de resserrer votre programme pour un système critique, alors le profilage peut être utile. pourrait vous conduire sur le chemin des arts sombres pour savoir ce que fait spécifiquement votre compilateur, mais cela ne devrait pas être l'approche par défaut.

0 votes

@Nim : Parce que ces conditions peuvent changer à tout moment. Elles ne sont pas vraiment fiables.

3voto

Pour Jesper : si l'objet à construire est grand, éviter la copie peut être nécessaire (ou à tout le moins hautement souhaitable).

Si le RVO se produit, la copie est évitée et vous n'avez pas besoin d'écrire d'autres lignes de code.

Si ce n'est pas le cas, vous devrez le faire manuellement, en écrivant vous-même un échafaudage supplémentaire. Et cela impliquera probablement de désigner un tampon à l'avance, ce qui vous obligera à écrire un constructeur pour cet objet vide (probablement invalide, vous pouvez voir comment cela n'est pas propre) et une méthode pour "construire" cet objet invalide.

Donc, 'Cela peut réduire mes lignes de code si c'est garanti'. N'est-ce pas ?" ne signifie pas que Masoud est un crétin. Malheureusement pour lui, la RVO n'est pas garantie. Vous devez tester si cela se produit et si cela ne se produit pas, écrire l'échafaudage et polluer votre conception. Il ne peut pas être herpès.

3voto

RnMss Points 400

Sémantique des déplacements (nouvelle fonctionnalité de C++11) est une solution à votre problème, qui vous permet d'utiliser les éléments suivants Type(Type &&r); (le Constructeur de mouvement ) explicitement, au lieu de Type(const Type &r) (le Constructeur de copie ).

Par exemple :

class String {
  public:    
    char *buffer;

    String(const char *s) { 
      int n = strlen(s) + 1;
      buffer = new char[n];
      memcpy(buffer, s, n);
    }

    ~String() { delete [] buffer; }

    String(const String &r) { 
      // traditional copy ...
    }

    String(String &&r) {
      buffer = r.buffer; // O(1), No copying, saves time.
      r.buffer = 0;
    }
};

String hello(bool world) {
  if (world) {
    return String("Hello, world.");
  } else {
    return String("Hello.");
  }
}

int main() {
  String foo = hello();
  std::cout <<foo.buffer <<std::endl;
}

Et cela ne déclenchera pas le constructeur de copie .

0 votes

-1 parce que si vous utilisez return std::move(temp_object), l'objet peut sortir du champ d'application (exemple facile, T t ; return std::move(t) ;) et vous vous retrouverez avec une référence invalide. Comme vous utilisez String dans cet exemple, cela POURRAIT fonctionner car les Strings sont conservés dans un pool et ne peuvent pas être supprimés même s'ils sont hors de portée, mais ne vous y fiez pas en général.

0 votes

@WorldSEnder Je ne vois pas de référence invalide ici. return -L'ajout d'un objet permet de construire un nouvel objet (en le copiant ou en le déplaçant), et non une référence à l'objet temporaire. Ce que vous dites ne se produit que lorsque le type de retour est une référence ou un pointeur.

0 votes

Vous avez raison si vous ne retournez pas String&&. Merci, je pensais que c'était différent.

0voto

Jesper Points 3116

Je n'ai pas de réponse par oui ou par non, mais vous dites que vous pouvez écrire moins de lignes de code si l'optimisation recherchée est garantie.

Si vous écrivez le code que vous devez écrire, le programme fonctionnera toujours, et si l'optimisation est là, il fonctionnera plus rapidement. S'il y a effectivement un cas où l'optimisation "remplit le vide" dans la logique plutôt que dans la mécanique du code et le fait fonctionner, ou change carrément la logique, cela semble être un bogue que je voudrais voir corrigé plutôt qu'un détail d'implémentation sur lequel je voudrais compter ou que je voudrais exploiter.

0 votes

Les votes négatifs ne me dérangent pas, mais veuillez expliquer pourquoi. Je ne vois pas ce qu'il y a de mal à dire "s'il vous plaît, ne faites pas reposer votre code sur des optimisations".

0 votes

Je ne vous ai pas descendu, mais je ne suis pas sûr que cela réponde vraiment à la question. C'est un peu comme si on la survolait :)

0 votes

C'est juste. Parfois, la réponse est vraiment "s'il vous plaît, mon Dieu, ne faites pas ça", même si je pense que cette question est loin d'en être une.

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