L'efficacité du passage de std::vector
Quand une fonction C++ accepte un std::vector
argument, le schéma habituel est de passer par const
de référence, tels que:
int sum2(const std::vector<int> &v)
{
int s = 0;
for(size_t i = 0; i < v.size(); i++) s += fn(v[i]);
return s;
}
Je crois que ce code entraîne une double référence lorsque les éléments du vecteur sont accessibles, parce que le PROCESSEUR doit d'abord déréférencement de v
de lire le pointeur vers le premier élément, le pointeur doit être déréférencés de nouveau à lire le premier élément. Je m'attends à ce qu'il serait plus efficace de passer d'une copie de l'objet vectoriel sur la pile. Une telle copie superficielle résumerait un pointeur vers le premier élément, et la taille, avec le pointeur de la référence à la même zone de mémoire que le vecteur d'origine n'.
int sum2(vector_ref<int> v)
{
int s = 0;
for(size_t i = 0; i < v.size(); i++) s += fn(v[i]);
return s;
}
Des performances similaires, mais beaucoup moins la commodité pourrait être obtenu par le passage d'un itérateur à accès aléatoire paire. Ma question est: quel est vicié par cette idée? J'attends qu'il devrait y avoir une bonne raison que les gens intelligents accepter de payer la performace coût de vecteur de référence, ou de traiter avec l'inconvénient d'itérateurs.
Edit: sur la Base de commentaires ci-dessous, veuillez tenir compte de la situation si je il suffit de renommer l'a suggéré vector_ref
classe de tranche ou gamme. L'intention est d'utiliser random-access itérateur les couples avec plus de naturel de la syntaxe.
- Avez-vous fait contre le code machine généré dans les deux cas? Ou mesuré les performances?
- Avez-vous vérifié qu'il en est bien ainsi dans l'assemblée? Aussi, je ne pense pas que la convention de itérateur paires vient de considérations de performance, c'est la conception de décision (et en effet boost est en favorisant un beaucoup plus commode concept d'itérateur gamme d'objets, même si je n'ai pas étudié la façon dont ils agissent exactement).
- Je ne pense pas que vos commentaires en fait aucun sens. Une référence est un alias vers un objet. En tant que tel, il n'est pas plus cher pour accéder à une référence à un objet en tant qu'il serait à accède à l'objet d'origine. Cette légèrement inflammatoire diatribe est de la spéculation de votre part, si vous aviez fait deux minutes de la diligence raisonnable, vous n'auriez pas besoin de ce post.
- une référence est un pointeur avec la syntaxe du sucre sur elle. Sérieusement.
- Une référence est un pointeur qui ne peut pas être réinstaller (point à un autre objet). Le compilateur peut être en mesure d'optimiser le code qui utilise une référence au lieu d'un pointeur de façon plus efficace en raison de cette propriété. Mais une référence est en effet tout à fait différent de l'objet lui-même.
Vous devez vous connecter pour publier un commentaire.
Pas nécessairement. Les compilateurs sont assez intelligents et devrait être en mesure de éliminer les sous-expressions. Ils peuvent voir que l'opérateur
[]
ne change pas le "pointeur vers le premier élément", ils n'ont donc pas besoin de faire de la CPU le recharger à partir de la mémoire pour chaque itération de boucle.Ce qui est erroné avec votre idée est que vous avez déjà deux très bonnes solutions:
Bien sûr, vous pouvez faire valoir que l'itérateur paire est "moins syntaxe naturelle", mais je suis en désaccord. Il est parfaitement naturel pour tous ceux qui ont utilisé à la STL. Il est efficace, et vous donne exactement ce dont vous avez besoin pour travailler avec la gamme, en utilisant std algorithmes ou de vos propres fonctions.
Itérateur paires sont un common C++ langage, et un programmeur C++ lecture de votre code comprendrez sans problème, alors qu'ils vont être surpris de voir à votre brassée maison de vecteur wrappers.
Si vous êtes vraiment paranoïaque à propos de la performance, de passer à la paire d'itérateurs. Si la syntaxe vraiment vous dérange, passez le vecteur et la confiance du compilateur.
Simple: C'est l'optimisation prématurée. Solutions: Accepter un
vector<int> const&
et utiliser des itérateurs ou passer des itérateurs directement à la fonction.v[i]
etint* p = &v[0]; ... p[i]
et d'en faire rapport. 🙂Vous avez raison, il y a une indirection supplémentaire ici. Il est envisageable (même si ce serait surprenant) si le compilateur (avec l'aide de lien-temps de génération de code optimisé loin.
Ce que vous avez proposé est parfois appelé le tranchage, et il est largement utilisé dans certaines situations. Même si, en général, je ne suis pas sûr qu'il vaut la peine de les dangers. Vous devez être très prudent au sujet de invaliding votre tranche (ou quelqu'un d'autre).
Noter que si vous avez utilisé les itérateurs de la boucle au lieu de l'indexation, alors vous feriez deref la référence seulement une couple de fois (pour appeler
begin()
etend()
) plutôt que de n fois (à l'index dans le vecteur).(Je suis en supposant que l'optimiseur de hisser le
end()
les appels en dehors de la boucle. Vous pourriez le faire explicitement pour en être certain.)Passer une paire d'itérateurs plutôt que sur le contenant lui-même ressemble à la STL de l'idiome. Que serait vous donner plus de portée générale, comme le type de conteneur peut varier, mais le nombre de déréférence nécessaire.
Passage par valeur, sauf si vous êtes certain passage par référence d'améliorer les performances.
Lorsque vous passez par valeur, copie élision peut se produire qui aura pour résultat similaire si pas de meilleures performances.
Dave a écrit à ce sujet ici:
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
Il n'y a pas de double de déférence, car le compilateur va probablement passer le réel pointeur vers le vecteur de l'argument et non un pointeur vers un pointeur. Vous pouvez simplement essayer et vérifier le démontage de la vue de votre IDE de ce qui se passe réellement derrière les coulisses:
Ce qui se passe ici? Le vecteur est alloué sur la pile. La première repousser est traduite en:
Maintenant, nous Méthode call(), en passant le vecteur const&:
Sans surprise, le pointeur vers le vecteur est passé comme références ne sont rien, mais de façon permanente pointeurs déréférencés. Maintenant à l'intérieur de la Méthode(), le vecteur est accessible à nouveau:
Le vecteur de pointeur est pris directement à partir de la pile et de l'écrit à ecx.