7
votes

C ++ Vecteur AT / [] Vitesse de l'opérateur

Pour donner aux fonctions, la possibilité de modifier le vecteur, je ne peux pas faire xxx

mais je dois faire: xxx

(comme les réponses de mon autre question soulignée)

  • Je vais faire un enfer beaucoup d'appels vers myvec.at () alors. À quelle vitesse est-il, comparé au premier exemple en utilisant une variable pour stocker le résultat?

  • y a-t-il une option différente pour moi? Puis-je utiliser des pointeurs d'une manière ou d'une autre?

    Quand il devient sérieux, il y aura des milliers d'appels vers myvec.at () par seconde. Donc, chaque petit mangeur de performance est important.


9 commentaires

Outre les considérations de performance, demandez-vous ce que vous obtenez en utilisant at () plutôt que [] . La vérification des limites sur les indices est agréable pour le débogage mais [] aussi est-ce lorsque vous compilez en mode de débogage, et en utilisant à () au lieu de < Code> [] n'est pas un substitut de l'écriture du code correct, et c'est redondant si le code est déjà correct.


@ Unicorn à nez rouge: Tous les compilateurs Vérifiez [] en mode de débogage.


@Brian: Malheureusement, c'est vrai. Et tu sais quoi? Que suce . Mais heureusement, cela peut être réparé en installant une version alternative de la bibliothèque standard - par exemple stlport: stlport.org .


@ Licorne à nez rouge: Non seulement le commentaire de Brian est correct, mais le test en mode de débogage ne trouve pas tous les problèmes. Il pourrait bien valoir la peine d'utiliser .at () au lieu de [] dans le code de production, en fonction de beaucoup de choses.


@David: Je suis en désaccord. Je suis d'accord pour dire qu'il peut être bénéfique d'avoir un système d'exécution vérifié (comme dans Java ou .NET). Mais je ne suis pas d'accord que changer les itérateurs et [] pour une variante ostensiblement sûre est bonne. Personne n'atteint at () erreurs d'exécution quand même et les personnes qui do saisent l'erreur peuvent aussi bien utiliser [] et vérifier explicitement un index valide à l'avance. . Le seul avantage de at () sur [] est qu'un programme de buggy s'écrasera au lieu de travailler sur une mémoire invalide. Si vous préférez que (ce qui est parfaitement raisonnable), utilisez un système d'exécution qui donne le comportement souhaité ...


(suite) et remarquez que la norme C ++ est absolument Permet un tel système d'exécution vérifié. Rien ne parle contre la fabrication [] une opération vérifiée.


Les gens attrapent at () erreurs et ceci est souvent préférable aux vérifications manuelles avant d'appeler opérateur [] sur chaque site d'appel. Le point est que C ++ vous donne le choix. Si vous avez besoin d'une vitesse, vous utilisez opérateur [] .


Crashing de manière définie est Loin meilleur comportement que de piétinement dans une mémoire aléatoire. Oui, cela pourrait être gênant, mais cela empêche toutes sortes d'attaques dans le code de la sécurité-critique. Pour désquier Ben Franklin, «ceux qui peuvent abandonner la sécurité essentielle pour obtenir une petite vitesse temporaire, ne méritent ni la sécurité ni la rapidité.»


Pour vous citer: «Ils veulent que Java sachent où le trouver».


9 Réponses :


2
votes

opérateur [] peut être plus rapide que à code>, car il n'est pas nécessaire de faire la vérification des limites.

Vous pouvez faire curr code> une référence pour faire ce que vous Vous voulez. P>

MyClass & curr = myvec.at(i);


0 commentaires

18
votes

Vous pouvez utiliser une référence: xxx

la fonction de membre à Les limites vérifient pour s'assurer que l'argument est dans la taille du vecteur < / code>. Le profilage n'est qu'un moyen de savoir exactement combien plus lentement est comparé à opérateur [] . En utilisant une référence ici vous permet de faire la recherche une fois de la recherche, puis d'utiliser le résultat dans d'autres endroits. Et vous pouvez en faire une référence à- const si vous souhaitez vous protéger de la valeur de modification accidentelle de la valeur.


2 commentaires

Dépend de quoi "donner des fonctions la possibilité de modifier le vecteur" signifie. Certaines modifications de vecteur invalident les références - tout ce qui provoque la réaffectation.


@Steve, c'est un bon point. Je suppose que l'OP modifie valeurs dans le vecteur basé sur le code exemple donné. Si les fonctions peuvent modifier le vecteur lui-même, nous aurons besoin d'une approche différente.



2
votes

Lorsque la performance est un problème, il n'y a pas de substitut au profilage. Les capacités d'optimisation des compilateurs changent de la version à la version, et minuscules altérations insignifiantes du code source peuvent modifier considérablement la performance résultante.

Personne ne peut répondre à cette question, mais vous-même: créer un harnais de test et lancer plusieurs algorithmes et voir ce que vous obtenez.

ps. Si la performance est vraiment un problème, j'ai eu une augmentation du facteur 10 augmentation de la vitesse d'un décodeur PNG en supprimant les vecteurs et en les remplaçant avec des matrices brutes. Encore une fois, c'était pour Visual Studio 6. Je ne prétends pas que la substitution de la matrice brute vous donnera une amélioration du facteur 10, mais c'est quelque chose à essayer.


2 commentaires

Je ne sais pas à propos de VS6 (heureusement, j'ai pu utiliser GCC à l'époque), mais une incréments de vitesse de dix vitesses ressemble à une grande différence. Utilisez-vous les mêmes algorithmes dans les deux cas? La taille de vecteur a-t-elle été allongée dans la mise en œuvre? La mise en œuvre actuelle d'un vecteur dans g ++ contient trois pointeurs, à commencer () , fin () et commence () + capacité , sauf si vous activez Itérateurs vérifiés, les opérations sur un vecteur sont aussi rapides que les opérations sur les données brutes.


L'algorithme était pareil. Nous venons de modifier les vecteurs d'octets en matrices brutes d'octets. Les fonctions ont été modifiées pour prendre des pointeurs et les entiers nécessaires étant la taille de la matrice. Certaines boucles ont été déployées dans des memcpy.



0
votes

La complexité de à () est constante, c'est-à-dire en pratique, cela signifie qu'il doit être conçu pour ne pas avoir de pénalité de performance pertinente.

Vous pouvez utiliser [] , qui est également une complexité constante, mais ne vérifie pas les limites. Cela équivaudrait à utiliser l'arithmétique du pointeur et, donc potentiellement un peu plus vite que le premier.

Dans tous les cas, le vecteur est spécialement conçu pour un accès permanent performant à l'un de ses éléments. Donc, cela devrait être le moindre de vos soucis.


0 commentaires

2
votes

La raison pour laquelle le premier ne fonctionne pas est que vous ne définissez pas un pointeur ou un itérateur à l'adresse de la variable ith. Au lieu de cela, vous réglez cur, égal à la valeur de la variable, puis modifiant. Je suppose que Dothis et Dothat sont des références.

Faites ceci: P>

MyObject& curr = myvec.at( i );


0 commentaires

1
votes

options que je vois, à peu près ordre de préférence inverse :

  1. Stockez les pointeurs dans votre conteneur au lieu des objets réels. Cela peut être souhaitable de toute façon, si les objets sont suffisamment complexes que les copier autour sont problématiques.
  2. Utilisez l'opérateur d'indexation [] au lieu de at () .
  3. juste appelez at () une fois et enregistrez-le dans une référence (voir la réponse de Kristo ci-dessus).
  4. oubliez-le jusqu'à ce que vous ayez un problème d'exécution excessive. Si cela se produit, profilez d'abord de votre code pour vous assurer que le goulot d'étranglement est ici et ne vous inquiétez que de faire l'une des ci-dessus. accélère les choses.

    Honnêtement, ce que vous devriez faire est de jouer avec les quatre approches différentes et utilisez simplement celui qui produit le code le plus facile à comprendre. Dans la plupart des cas, nous sommes heureux de sacrifier quelques cycles de machine pour le code qui est plus facile pour les êtres humains à entretenir.


0 commentaires

4
votes

de mes propres tests avec un code similaire (compilé sous GCC et Linux), opérateur [] peut être sensiblement plus rapide que à , pas à cause de la vérification des limites, mais à cause des frais généraux de la manipulation des exceptions. Remplacer à (qui jette une exception sur les limites) avec mes propres limites vérifiant qui a augmenté un Aster sur des limites inutiles a donné une amélioration mesurable.

Utilisation d'une référence, comme Kristo a déclaré , vous permet que engendrer les limites vérifiant la tête une fois.

Ignorer la vérification des limites et la manipulation des exceptions des frais généraux, les deux opérateur [] et au doivent être optimisés à l'accès à une gamme directe ou à un accès direct via le pointeur.

Comme Chris Becke dit, cependant, il n'y a pas de substitut au profilage.


3 commentaires

En fait, des exceptions déclenchantes sont généralement lentes et la vérification ne sont pas nécessairement trop lentes.


@Martin: Cela dépend beaucoup de la mise en œuvre. En raison de quelques-uns, c'est la pile de détente qui coûte cher, pas le "déclenchement" (lancer)


@Msalters - Oui, mais le test n'est pas nécessairement cher si l'exception n'est pas exceptionnelle.



0
votes

Les vecteurs sont les plus adaptés à la vitesse d'accès. L'accès à un élément aléatoire dans un vecteur est de complexité O (1) par rapport à O (n) pour les listes de liaison générales et o (log n) pour les arbres de liaison.

Cependant, cette question est mal placée car la réponse à votre autre question vous a égaré en n'expliquant pas comment corriger votre problème d'origine en utilisant une référence.


0 commentaires

0
votes

Si c'est le cas, que vous chargez un vecteur, procédez-la sans ajouter ou supprimer plus d'éléments, puis envisagez d'obtenir un pointeur sur la matrice sous-jacente et utilisez des opérations de réseau sur cela pour «éviter le surcharge de vecteur». .

Si vous ajoutez ou supprimez des éléments dans votre traitement, cela n'est pas sûr de le faire, car la matrice sous-jacente peut être déplacée à tout moment du vecteur lui-même.


0 commentaires