L’utilisation de l’automobile de C ++ 11 peut-elle améliorer les performances?

Je peux voir pourquoi le type auto en C ++ 11 améliore l’exactitude et la maintenabilité. J’ai lu que cela peut aussi améliorer les performances ( Almost Always Auto de Herb Sutter), mais une bonne explication me manque.

  • Comment améliorer auto les performances?
  • Quelqu’un peut-il donner un exemple?

auto peut aider les performances en évitant les conversions implicites silencieuses . Un exemple que je trouve convaincant est le suivant.

 std::map m; // ... for (std::pair const& item : m) { // do stuff } 

Voir le bug? Nous sums ici, pensant que nous prenons élégamment chaque élément de la carte par référence const et en utilisant la nouvelle expression de scope pour clarifier notre intention, mais en réalité, nous copions chaque élément. C’est parce que std::map::value_type est std::pair , pas std::pair . Ainsi, lorsque nous avons (implicitement):

 std::pair const& item = *iter; 

Au lieu de prendre une référence à un object existant et de le laisser, nous devons faire une conversion de type. Vous êtes autorisé à prendre une référence const à un object (ou temporaire) d’un type différent tant qu’il existe une conversion implicite, par exemple:

 int const& i = 2.0; // perfectly OK 

La conversion de type est une conversion implicite autorisée pour la même raison que vous pouvez convertir une const Key en une Key , mais nous devons construire un temporaire du nouveau type pour permettre cela. Ainsi, notre boucle fonctionne efficacement:

 std::pair __tmp = *iter; // construct a temporary of the correct type std::pair const& item = __tmp; // then, take a reference to it 

(Bien sûr, il n’y a pas vraiment d’object __tmp , il est juste là pour l’illustrer, en réalité le temporaire sans nom est juste lié à l’ item pour sa durée de vie).

Il suffit de changer pour:

 for (auto const& item : m) { // do stuff } 

nous a juste sauvé une tonne de copies – maintenant le type référencé correspond au type d’initialiseur, donc aucun temporaire ou conversion n’est nécessaire, nous pouvons juste faire une référence directe.

Comme auto déduit le type de l’expression d’initialisation, aucune conversion de type n’est impliquée. Combiné à des algorithmes basés sur des modèles, cela signifie que vous pouvez obtenir un calcul plus direct que si vous créiez un type vous-même, en particulier lorsque vous avez affaire à des expressions dont vous ne pouvez pas nommer le type!

Un exemple typique vient de (ab) en utilisant std::function :

 std::function cmp1 = std::bind(f, _2, 10, _1); // bad auto cmp2 = std::bind(f, _2, 10, _1); // good auto cmp3 = [](T a, T b){ return f(b, 10, a); }; // also good std::stable_partition(begin(x), end(x), cmp?); 

Avec cmp2 et cmp3 , l’algorithme entier peut aligner l’appel de comparaison, alors que si vous construisez un object std::function , non seulement l’appel ne sera pas mis en ligne, mais vous devrez aussi parcourir la recherche polymorphe dans l’intérieur effacé. de la fonction wrapper.

Une autre variante de ce thème est que vous pouvez dire:

 auto && f = MakeAThing(); 

C’est toujours une référence, liée à la valeur de l’expression d’appel de fonction et ne construit jamais d’objects supplémentaires. Si vous ne connaissiez pas le type de valeur renvoyé, vous pourriez être obligé de créer un nouvel object (peut-être temporaire) via quelque chose comme T && f = MakeAThing() . (En outre, auto && fonctionne même lorsque le type de retour n’est pas mobile et que la valeur de retour est une valeur.)

Il y a deux catégories.

auto peut éviter l’effacement de type. Il existe des types non modifiables (comme lambdas), et des types presque inanimables (comme le résultat de std::bind ou d’autres types d’expressions).

Sans auto , vous finirez par devoir taper les données jusqu’à quelque chose comme std::function . Le type d’effacement a des coûts.

 std::function task1 = []{std::cout << "hello";}; auto task2 = []{std::cout << " world\n";}; 

task1 a une task1 type effacement - une allocation de tas possible, une difficulté à l'installer et une surcharge d'invocation de table de fonction virtuelle. task2 n'en a pas. Les Lambdas ont besoin d'une auto ou d'autres formes de déduction de type pour stocker sans effacement de type; d'autres types peuvent être si complexes qu'ils n'en ont besoin que dans la pratique.

Deuxièmement, vous pouvez obtenir des types incorrects. Dans certains cas, le mauvais type fonctionnera parfaitement, mais provoquera une copie.

 Foo const& f = expression(); 

comstackra si expression() retourne Bar const& ou Bar ou encore Bar& , où Foo peut être construit à partir de Bar . Un Foo temporaire sera créé, puis lié à f , et sa durée de vie sera prolongée jusqu'à ce que f disparaisse.

Le programmeur a peut-être voulu dire Bar const& f et ne pas avoir l'intention d'y faire une copie, mais une copie est faite indépendamment.

L'exemple le plus courant est le type de *std::map::const_iterator , qui est std::pair const& not std::pair const& , mais l'erreur est une catégorie d'erreurs dont les performances sont silencieuses. Vous pouvez construire un std::pair partir d'un std::pair . (La clé sur une carte est const, car l'édition est une mauvaise idée)

@Barry et @KerrekSB ont d'abord illustré ces deux principes dans leurs réponses. Ceci est simplement une tentative de mettre en évidence les deux questions en une seule réponse, avec une formulation qui vise le problème plutôt que d'être centrée sur l'exemple.

Les trois réponses existantes donnent des exemples où l’utilisation de auto aide auto «rend moins susceptible de pessimiser involontairement», ce qui en fait «améliore les performances».

Il y a un revers à la pièce. L’utilisation d’ auto avec des objects dont les opérateurs ne renvoient pas l’object de base peut entraîner un code incorrect (toujours compilable et exécutable). Par exemple, cette question demande comment utiliser auto donne des résultats différents (incorrects) en utilisant la bibliothèque Eigen, à savoir les lignes suivantes

 const auto resAuto = Ha + Vector3(0.,0.,j * 2.567); const Vector3 resVector3 = Ha + Vector3(0.,0.,j * 2.567); std::cout << "resAuto = " << resAuto <Google Analytics a-t-il des problèmes de performances?
  • boolean vs. BitSet: Quel est le plus efficace?
  • Performances de la boucle C 64 bits sur x86
  • Où la mémoire cache L1 des processeurs Intel x86 est-elle documentée?
  • traduction3d vs traduction performance
  • Pourquoi Go est-il si lent (comparé à Java)?
  • Quel est le but du registre de pointeur de trame EBP?
  • Outils d'parsing des performances d'un programme Haskell
  • Performances des méthodes statiques vs méthodes d'instance
  • Un moyen efficace d'effectuer des INSERTS par lots avec JDBC
  • Comparaison entre RabbitMQ et MSMQ