7
votes

chaîne [x] vs * string ++

Laquelle des 2 méthodes est théoriquement plus rapide et pourquoi? (le pointeur sur la chaîne doit être constant.)

Quelle est la différence exacte entre destination [Nombre] et * em> destination ++ ? destination [Nombre] continue de se déplacer de 0 to comptez sur chaque appel? Est-ce que * Destination ++ Il suffit d'ajouter 1 sur chaque appel? xxx

c

0 commentaires

8 Réponses :


6
votes

Cela dépend du compilateur. Avec des compilateurs modernes, il n'est vraiment pas possible de prédire quoi que ce soit sur l'optimisation sur le niveau d'instruction unique sans regarder en réalité le code généré. Tous deux compilent probablement des instructions équivalentes.

qui dit, Destination [Nombre] est pas boucle de 0 à Nombre sur chaque appel. Il prend l'emplacement de la mémoire de destination [0] , ajoute compte fois la taille du type de * destination et regarde là-bas. (C'est tout naïvement parlant, bien sûr - le compilateur fait probablement quelque chose de plus rapide.)

D'autre part, * Destination ++ prend l'emplacement de la mémoire de la destination <0] (qui est pas le début de la matrice , comme vous avez changé destination ), regarde là et ajoute la taille du type de * destination de sorte que destination [0] fait référence à quoi l'habitude d'être destination [1] .


0 commentaires

5
votes

Dans le bon vieux temps, la deuxième méthode est plus rapide. Mais avec l'optimisation moderne du compilateur, ils sont exactement identiques .

Ceci est parce que: xxx

ferait < Pré> xxx

Il prend deux autres Ajouter Opération dans chaque boucle. Le compilateur moderne ferait cela pour vous.


0 commentaires

0
votes

* destination ++ ajoute réellement Tailleof (* destination) à chaque appel (cela dépend de la taille du pointeur) tandis que destination [Compte] devrait faire * (destination + compte * Taille de (* destination)) Mais il est probablement optimisé par le compilateur de toute façon.


0 commentaires

0
votes

Destination [Nombre] est le Nombre -th élément (0 basé sur) dans destination [] . Nombre ++ garantit que, à chaque fois que la boucle est entrée, destination [Count] est un autre élément accédé à l'itération précédente (sauf, bien sûr, la première entrée).

* destination ++ = * PTR ++; est identique à celui suivant: xxx

Notez que Destination ++ incrémente le pointeur Valeur par Tailleof (* Destination) , qui pointera sur l'élément suivant dans la matrice, pas les données pointées par destination (ou * destination ).

Pour les compilateurs modernes, les deux seront optimisés à presque la même binaire, donc peu importe laquelle que vous choisissez par rapport à la vitesse.


1 commentaires

Désolé, "Notez que la destination ++ incrémente la valeur du pointeur par 1", ajoute réellement Tailleof (* destination) , qui peut ne pas être 1



0
votes

Si "String" est disponible comme un argument dans une fonction, vous pouvez l'utiliser comme itérateur au lieu de "PTR". Ensuite, vous aurez une moins variable pour tenir sur la pile. Cela pourrait éventuellement dire que le compilateur peut conserver toutes les variables locales dans des registres, et cela améliorerait les performances.

Cela dépend bien sûr, si le compilateur parvient à supprimer "Compter", vous n'aurez pas d'incréments de performance.


0 commentaires

0
votes

Le regardant à partir d'un niveau élevé, les deux opérations impliquent une déréence et une addition. L'expression destination [Count] est évaluée sous forme * (destination + compte) . L'expression * destination ++ est (très probablement) évaluée comme une destination *; Destination + = 1 (Gardant à l'esprit que l'effet secondaire doit seulement être appliqué avant le point de séquence suivant, ce qui n'est pas nécessairement immédiatement immédiatement après l'évaluation de l'expression).

Si théoriquement, il ne devrait pas y avoir aussi de différence entre les deux versions. La seule façon de savoir pour votre plate-forme particulière est de coder les deux versions et de comparer leurs performances.


0 commentaires

2
votes

dépend de la CPU. Sur un X86, la première version est légèrement plus rapide (ou, au moins, elle entraînera un code légèrement plus court), car il ne prend qu'une instruction de chargement à partir de chaîne [Count] et une instruction pour écrire dans la destination [Count]. Donc, le pseudocode ressemblerait à ce que ceci (chaque ligne = une instruction d'assemblage) xxx

et la deuxième version serait xxx

in Pratique, tout compilateur d'optimisation optimisera le diable hors de ce code et, selon sa sophistication, le compilateur peut même être capable de transformer la première version en seconde ou inversement. Donc il n'y a pas de dire lequel va être plus rapide.


0 commentaires

5
votes

Pourquoi devrions-nous spéculer? Nous pouvons l'essayer et le savoir. J'ai compilé le code avec GCC -O3 -g (sur x86) et désassemblé le résultat. Il y avait plus de changements que prévu, donc je me concentrerai sur les bits au milieu où nous nous attendions à ce que l'essentiel des différences entre les deux. Le noyau de la boucle dans le premier cas: xxx

le noyau de la boucle dans le second cas: xxx


sur Cette base, la seconde est peut-être un peu plus rapide. Mais vraiment, cela ne fera pas beaucoup de différence dans la pratique. Le cache L1 tient les deux boucles à la fois bien et la mémoire cible est incachetée afin que les différences soient discutées. Bonne chance avec toujours en train de mesurer une différence entre les deux.


5 commentaires

Fait moins d'instructions == plus vite sur x86?


@Jbrwilkinson: Pas nécessairement. Cela dépend vraiment de ce que font ces instructions, car l'accès à la mémoire principale est de loin plus lent que de fonctionner dans le cache L1. Comme je l'ai noté, je m'attendrais à ce que les vitesses soient très similaires dans la pratique (et l'heure globale de la fonction sera réellement dominée par l'appel à malloc ; c'est extrêmement plus lente que la copie car elle peut impliquer une recherche complexe ou un appel système).


Ça dépend. Voici quelques raisons pour lesquelles: Différentes instructions prennent des quantités différentes de cycles à compléter (et les instructions sont des tailles différentes). L'alignement des boucles peut avoir un impact significatif sur la vitesse (sautant 64 octets est bon, sautant 63 est moins bon). La réduction de la taille de votre code accélère souvent le code en utilisant mieux les caches intermédiaires. Cependant, les chaînes de dépendance et la prédiction des succursales sont deux prédicteurs puissants (mieux que le nombre d'instructions ou la taille du code) des performances de code.


@Brian: Oui, mais lorsque vous avez un mélange de code qui correspond à tous dans la gai de L1 et des données au moins partiellement incached, ce sera le temps d'accès aux données qui domine; Le processeur passera son temps à attendre que les opérations de données liées à la mémoire se produisent. Étant donné que le modèle d'accès aux données des deux extraits est le même (emplacements de mémoire équivalents, même ordre), je m'attendrais à ce que le temps pris soit très similaire. Et le malloc appel sera dominer.


hm .. Dans le premier cas, gcc veut conserver destination , donc il est attribué % ESI pour ce pointeur TEMP. C'est ce qui fait l'instruction supplémentaire.