2009-06-10 8 views
9

L'une des améliorations C++ 0x qui permettront d'écrire du code C++ plus efficace est le pointeur unique_ptr intelligent (dommage qu'il ne permette pas de passer par des opérations similaires à memmove(): la proposition n'a pas fait dans le projet).Améliorations des performances C++ 0x

Quelles sont les autres améliorations de performances dans la prochaine norme? prendre le code suivant par exemple:

vector<char *> v(10,"astring"); 
string concat = accumulate(v.begin(),v.end(), string("")); 

Le code concaténer toutes les chaînes contenues dans le vecteur v. Le problème avec ce morceau de code net est que accumulate() copie les choses autour, et n'utilise pas de références. Et la chaîne() se réalloue chaque fois que l'opérateur est appelé. Le code a donc des performances médiocres par rapport au code C analogique bien optimisé.

Est-ce que C++ 0x fournit des outils pour résoudre le problème et peut-être d'autres?

+1

Voici une autre réponse à propos du sujet: http://stackoverflow.com/questions/637695/how-efficient-is-stdstring-compared-to-null-terminated-strings/637737#637737 –

Répondre

13

Oui C++ résout le problème par quelque chose appelé move sémantique.

Fondamentalement, il permet à un objet de prendre la représentation interne d'un autre objet si cet objet est temporaire. Au lieu de copier chaque octet de la chaîne via un constructeur de copie, par exemple, vous pouvez simplement autoriser la chaîne de destination à prendre la représentation interne de la chaîne source. Ceci est autorisé uniquement lorsque la source est une valeur r. Cela se fait par l'introduction d'un constructeur de mouvement. C'est un constructeur où vous savez que l'objet src est temporaire et s'en va. Par conséquent, il est acceptable que la destination prenne la représentation interne de l'objet src.

La même chose est vraie pour opérateurs d'affectation de mouvement. Pour distinguer un constructeur de copie d'un constructeur de déplacement, le langage a introduit références rvalue. Une classe définit son constructeur de mouvement pour prendre une référence rvalue qui ne sera liée qu'à des rvalues ​​(temporaires). Donc, ma classe définirait quelque chose le long des lignes de:

class CMyString 
{ 
private: 
    char* rawStr; 
public: 

    // move constructor bound to rvalues 
    CMyString(CMyString&& srcStr) 
    { 
     rawStr = srcStr.rawStr 
     srcStr.rawStr = NULL;    
    } 

    // move assignment operator 
    CMyString& operator=(CMyString&& srcStr) 
    { 
     if(rawStr != srcStr.rawStr) // protect against self assignment 
     { 
      delete[] rawStr; 
      rawStr = srcStr.rawStr 
      srcStr.rawStr = NULL; 
     } 
     return *this; 
    } 

    ~CMyString() 
    { 
     delete [] rawStr; 
    } 
} 

Here est un très bon article et détaillé sur la sémantique de mouvement et la syntaxe qui vous permet de le faire.

+3

Ceci rend les opérations de permutation comme trier et faire tourner les collections de collections beaucoup plus rapidement. Je soupçonne que l'exemple le plus probable serait le tri d'une collection de chaînes. – Brian

+1

Ceci est aussi une très belle explication des références de rvalues: https://www.boostpro.com/trac/wiki/BoostCon09/RValue101 –

+0

Étendre (ou convertir) tous les conteneurs et les algorithmes pour déplacer la sémantique ressemble à une grande tâche, je me demande comment Est-ce que les choses vont et si la compatibilité avec le STL réel sera préservée sans efficacité commerciale. –

7

Une amélioration des performances sera générée expressions constantes, qui est introduit par le mot-clé constexpr.

constexpr int returnSomething() {return 40;} 

int avalue[returnSomething() + 2]; 

Ce code C++ n'est pas légal, car returnSomething() + 2 n'est pas une expression constante. Mais en utilisant le mot-clé constexpr, C++ 0x peut indiquer au compilateur que l'expression est une constante à la compilation.

+1

Plus de mots-clés ???! Nous avons déjà volatile, unsigned, restreindre, statique, const, enregistrer ... suis-je absent? –

+1

@TrevorBoydSmith: C++ a comme 60 mots-clés, il vous en manque une tonne –

1

Désolé - vous ne pouvez pas affirmer comme un fait que string concat = accumulate(v.begin(),v.end(), string(""));doit réaffecter. Une mise en œuvre simple sera, bien sûr. Mais les compilateurs sont tout à fait autorisés à faire la bonne chose ici.

C'est déjà le cas dans C++ 98, et C++ 0x continue d'autoriser à la fois les implémentations intelligentes et bêtes. Cela dit, la sémantique du déplacement rendra les implémentations intelligentes plus simples.

+0

Voici le corps accumulate() de gcc 4.1.1: for (; __first! = __last; ++ __ first) __init = __init + * __ premier; return __init; Vous avez raison dire que les implémentations sont optimisées - en fournissant des spécialisations.Pour la chaîne c'est partiellement possible, la spécialisation devrait: 1) prendre la chaîne comme référence ou pointeur - c'est difficile, car la signature de fonction est spécifiée par la norme 2) utiliser append() ou + = - c'est possible. Mais après tout, je pense que de nouveaux outils appropriés dans la langue permettront de résoudre le problème sans spécialisations si spécifiques. –

0
vector<string> v(10, "foo"); 
string concat = accumulate(v.begin(), v.end(), string("")); 

Cet exemple est tout simplement une mauvaise programmation, dans toute norme C++. Il est équivalent à ceci:

string tmp; 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 

11 C++ sémantique de mouvement ne fera que prendre soin de la partie « copier à nouveau le résultat dans tmp » de l'équation. Les copies initiales de tmp seront toujours des copies. Il est un classique Schlemiel the Painter's algorithm, mais encore pire que l'exemple habituelle en utilisant strcat en C.

Si accumulate juste utilisé += au lieu de + et = alors il aurait évité toutes ces copies.

Mais C++ 11 ne nous donne un moyen de faire mieux, tout en restant concis, en utilisant une fonction lambda:

string concat; 
for_each(v.begin(), v.end(), [&](const string &s){ concat += s; }); 

EDIT: Je suppose que l'implémentation d'bibliothèque standard pourrait choisir de mettre en œuvre accumulate avec mouvement sur l'opérande à +, donc tmp = tmp + "foo" deviendrait tmp = move(tmp) + "foo", et cela résoudrait à peu près ce problème. Je ne suis pas sûr qu'une telle implémentation serait strictement conforme. Ni GCC, MSVC, ni LLVM ne le font actuellement. Et comme accumulate est défini dans <numeric>, on peut supposer qu'il est conçu uniquement pour une utilisation avec des types numériques.

Questions connexes