Laquelle des 2 méthodes est théoriquement plus rapide et pourquoi? (le pointeur sur la chaîne doit être constant.) em> Quelle est la différence exacte entre destination [Nombre] em> et * em> destination ++ em>? destination [Nombre] EM> continue de se déplacer de 0 em> to comptez em> sur chaque appel? Est-ce que * Destination ++ EM> Il suffit d'ajouter 1 sur chaque appel? P>
8 Réponses :
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. P>
qui dit, D'autre part, Destination [Nombre] code> est pas em> boucle de 0 à Nombre code> sur chaque appel. Il prend l'emplacement de la mémoire de destination [0] code>, ajoute compte code> fois la taille du type de * destination code> et regarde là-bas. (C'est tout naïvement parlant, bien sûr - le compilateur fait probablement quelque chose de plus rapide.) P>
* Destination ++ Code> prend l'emplacement de la mémoire de la destination <0] code> (qui est pas em> le début de la matrice , comme vous avez changé destination code>), regarde là et ajoute la taille du type de * destination code> de sorte que destination [0] code> fait référence à quoi l'habitude d'être destination [1] code>. p>
Dans le bon vieux temps, la deuxième méthode est plus rapide. Mais avec Ceci est parce que: p> ferait p> < Pré> xxx pré> Il prend deux autres Ajouter CODE> Opération dans chaque boucle. Le compilateur moderne ferait cela pour vous. P> p>
* destination ++ code> ajoute réellement Tailleof (* destination) code> à chaque appel (cela dépend de la taille du pointeur) tandis que destination [Compte] code> devrait faire * (destination + compte * Taille de (* destination)) Code> Mais il est probablement optimisé par le compilateur de toute façon. P>
Notez que 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. p> p> Destination [Nombre] CODE> est le Nombre em> -th élément (0 basé sur) dans destination [] code>. Nombre ++ Code> garantit que, à chaque fois que la boucle est entrée, destination [Count] code> est un autre élément accédé à l'itération précédente (sauf, bien sûr, la première entrée). * destination ++ = * PTR ++; code> est identique à celui suivant: p> Destination ++ code> incrémente le pointeur Valeur par Tailleof (* Destination) Code>, qui pointera sur l'élément suivant dans la matrice, pas les données pointées par destination code> (ou * destination code>). P>
Désolé, "Notez que la destination ++ incrémente la valeur du pointeur par 1", ajoute réellement Tailleof (* destination) code>, qui peut ne pas être 1
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. P>
Cela dépend bien sûr, si le compilateur parvient à supprimer "Compter", vous n'aurez pas d'incréments de performance. P>
Le regardant à partir d'un niveau élevé, les deux opérations impliquent une déréence et une addition. 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 em> est de coder les deux versions et de comparer leurs performances. p> destination [Count] code> est évaluée sous forme * (destination + compte) code>. L'expression * destination ++ code> est (très probablement) évaluée comme une destination *; Destination + = 1 Code> (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). p>
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) et la deuxième version serait p> 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. P> p>
Pourquoi devrions-nous spéculer? Nous pouvons l'essayer et le savoir. J'ai compilé le code avec le noyau de la boucle dans le second cas: p> 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. P> p> GCC -O3 -g code> (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:
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 i> similaires dans la pratique (et l'heure globale de la fonction sera réellement dominée par l'appel à malloc code>; 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 code> appel sera i> dominer.
hm .. Dans le premier cas, gcc code> veut conserver destination code>, donc il est attribué % ESI code> pour ce pointeur TEMP. C'est ce qui fait l'instruction supplémentaire.