Optimisation élémentaire
Lorsqu'il s'agit d'optimisation, de nombreuses personnes sont dédaigneuses : « Les vitesses des ordinateurs sont si rapides maintenant, à quoi ça sert d'être quelques pour cent plus rapides ? Cela a du sens. Les résultats compilés par les compilateurs actuels ont été entièrement optimisés, sauf pour le développement de logiciels spécifiques tels que les graphiques, les images et le multimédia, une optimisation délibérée n'est pas nécessaire dans la plupart des cas, mais si les développeurs écrivent du code, cela a du sens. temps, vous aviez déjà conscience de l'optimisation. En complétant l'optimisation, vous pouvez assurer ou même améliorer l'efficacité du développement. Pourquoi pas ?
Bien entendu, la conception de l'algorithme est au cœur de l'optimisation. Dans la plupart des cas, l'efficacité d'exécution du programme est principalement déterminée par la compréhension globale du programme par le développeur, la conception de l'algorithme, etc. ! Mais parfois, l’optimisation des détails a aussi du sens !
De plus, dans de nombreux cas, ce type d'optimisation ne nécessite pas d'écrire du code directement via l'assembleur, mais dans ce cas, cela peut aussi refléter la supériorité de la maîtrise des connaissances en assemblage !
Comme les deux fonctions suivantes :
fonction GetBit(i : Cardinal ; n : Cardinal) : Booléen ;
commencer
Résultat := Boolean((i shr n) et 1);
fin;
fonction GetBit(i : Cardinal ; n : Cardinal) : Booléen ;
commencer
Résultat := Boolean((1 shl n) et i);
fin;
Code d'assemblage correspondant :
MOUVEMENT ECX, EDX
SHR EAX, CL
ET EAX, 01 $
MOUVEMENT ECX, EDX
MOV EDX, 01 $
SHL EDX, CL
ET EAX, EDX
Ils ont la même fonction, ils prennent tous la valeur d'un certain bit de i, renvoient True s'il vaut 1, et False s'il vaut 0 !
En apparence, vous pourriez penser que l'efficacité d'exécution des deux fonctions est la même, mais en fait il y a une différence. L'opération de décalage du premier programme est effectuée sur i. Selon la convention d'appel par défaut de Delphi, register, à ce moment, je La valeur est stockée dans le registre EAX, et l'opération de décalage peut être effectuée directement ; mais le deuxième programme est différent pour terminer l'opération de décalage sur la valeur immédiate 1, elle doit d'abord être transférée dans le registre, donc il doit y avoir une instruction supplémentaire ! Bien sûr, pas dans tous les cas, moins d'instructions seront nécessairement plus rapides que plus d'instructions. Lors d'une exécution spécifique, nous devons également prendre en compte des problèmes tels que le cycle d'horloge d'exécution des instructions et l'appariement des instructions (nous n'en parlerons pas plus tard). le problème indépendamment. Uniquement lorsque les comparaisons ne peuvent être effectuées que dans des environnements de code spécifiques.
Dans des circonstances normales, cette différence d’efficacité est trop négligeable, mais ce n’est jamais une mauvaise chose de rester attentif à l’optimisation lors de la programmation ! Si un tel code est situé dans la couche la plus interne d’une boucle et que N cycles d’horloge s’accumulent dans un grand nombre de boucles, la différence d’efficacité d’exécution peut devenir très importante !
Ce qui précède n'est qu'un petit exemple. On peut voir que si vous pouvez réfléchir à certains problèmes du point de vue de l'assemblage pendant le développement, vous pouvez écrire du code détaillé plus efficace dans des langages de haut niveau tout en garantissant l'efficacité du développement ! Mais il arrive encore souvent qu'une optimisation détaillée doive être réalisée à l'aide de code assembleur intégré, et parfois, grâce à l'application de code assembleur intégré, l'écriture de code peut également devenir plus efficace.
Si vous devez inverser l’ordre des octets d’un nombre à 32 chiffres, comment pouvez-vous le faire complètement en langage de haut niveau dans Delphi ? Vous pouvez utiliser le décalage, vous pouvez également appeler la fonction intégrée Swap plusieurs fois, mais si vous pensez à une instruction BSWAP, tout devient très simple.
fonction SwapLong(Valeur : Cardinal) : Cardinal ;
asme
BSWAP-EAX
fin;
Remarque : comme ci-dessus, la valeur de Value est stockée dans le registre EAX et la valeur à 32 chiffres est également renvoyée via EAX, une seule phrase est donc nécessaire.
Bien sûr, la plupart des optimisations d'assemblage embarquées ne sont pas si simples, mais il est difficile d'obtenir une optimisation plus approfondie avec le peu de connaissances en assemblage apprises à l'université. L'expérience ne peut être acquise que grâce à l'accumulation et à la comparaison continues des codes d'assemblage compilés ! Heureusement, dans la plupart des cas, l’optimisation détaillée ne constitue pas l’élément principal de la conception d’un programme.
Cependant, si le programme développé implique des graphismes, des images, du multimédia, etc., encore faut-il procéder à une optimisation plus approfondie ! Heureusement, Delphi6 peut fournir un bon support qu'il s'agisse de l'optimisation des instructions à virgule flottante ou de l'application de MMX, SSE, 3DNow, etc. Même si vous souhaitez que les versions antérieures de Delphi prennent en charge ces jeux d'instructions étendus du processeur ou si vous souhaitez prendre en charge de nouveaux jeux d'instructions du processeur à l'avenir, vous pouvez utiliser les quatre instructions d'assemblage DB, DW, DD et DQ prises en charge par Delphi dans l'assemblage intégré ( dans le manuel officiel Delphi6 de Borland, le manuel du langage indique seulement qu'il prend en charge DB, DW et DD) et la représentation numérique des instructions pertinentes peut également être implémentée de manière flexible.
comme:
DW $A20F //CPUID
DW 770F$ //EMMS
BD $0F, $6F, $C1 //MOVQ MM0, MM1
Comprendre les instructions n'est que la base. Après avoir conçu l'algorithme autour de FPU, MMX et SSE, si vous souhaitez l'optimiser davantage, vous devez également comprendre certaines des caractéristiques techniques du CPU lui-même.
Jetons un coup d'œil aux deux morceaux de code suivants :
asme
AJOUTER [a], ECX
AJOUTER [b], EDX
fin
asme
MOV EAX, [a]
MOUVEMENT EBX, [b]
AJOUTER EAX, ECX
AJOUTER EBX, EDX
MOV [a], EAX
MOV [b], EBX
fin
Le second est-il plus efficace ? Faux, comme mentionné ci-dessus, moins d'instructions ne signifie pas une efficacité d'exécution élevée. Selon les informations pertinentes, le cycle d'horloge pour l'exécution des deux instructions dans la première section de code est de 3 (chaque instruction doit effectuer trois étapes de lecture, étape de modification et d'écriture), les cycles d'horloge exécutés par les six instructions de la deuxième section de code sont tous 1. Donc les deux morceaux de code sont tout aussi efficaces ? C'est encore faux, le deuxième morceau de code s'exécute en fait plus efficacement que le premier morceau de code ! Pourquoi? Étant donné que les processeurs après la classe Pentium disposent de deux pipelines pour exécuter les instructions, lorsque deux instructions adjacentes peuvent être appariées, elles peuvent être exécutées en même temps ! Spécifique aux deux morceaux de code ci-dessus, quelles en sont les raisons spécifiques ?
Bien que les deux instructions du premier code puissent être appariées, le cycle d'horloge d'exécution total requis est de 5 au lieu de 3, tandis que les six instructions du deuxième code peuvent être exécutées en parallèle, ce qui conduit à ce résultat.
En parlant de cela, ce sont tous des exemples très simples, qui en eux-mêmes ne peuvent pas vous être d’une grande aide. Si vous souhaitez vraiment optimiser un programme spécifique, vous devriez rechercher des articles spéciaux sur l'optimisation FPU et MMX, ou trouver des manuels techniques pour étudier et étudier des technologies telles que « l'exécution dans le désordre » et la « prédiction de branche ». J'espère juste que tous mes amis qui sont à l'université ne se concentreront pas uniquement sur ces outils de développement « générateurs d'argent » et les nouvelles technologies à la mode, mais pourront consacrer plus de temps à jeter les bases. Avec une base solide, vous pourrez rapidement maîtriser de nouvelles connaissances. , Ce n'est qu'ainsi que nous pourrons maîtriser de nouveaux outils et compétences de développement plus rapidement... (omettre mille mots).
Mais là encore, les connaissances doivent encore être utilisées pour résoudre des problèmes pratiques. Si vous vous concentrez quotidiennement sur les détails techniques, vous pouvez devenir un excellent hacker, mais vous ne développerez jamais de logiciels de premier ordre. L’objectif fondamental doit donc rester la création de valeur. Alors... je n'en parlerai plus, ça ne ressemblera plus vraiment à un article technique si j'en parle plus. ^_^
Pièce jointe : en plus de prendre en compte l'efficacité d'exécution, l'optimisation du programme doit également prendre en compte les problèmes de taille (une petite taille peut charger la mémoire plus rapidement et terminer le décodage des instructions et d'autres tâches plus rapidement. Par exemple, pour effacer le registre EAX, utilisez SUB EAX, EAX ou XOR EAX). , EAX au lieu de MOV EAX, $0 Bien que leurs cycles d'horloge d'exécution soient tous deux égaux à 1, la longueur de l'instruction du premier (2 octets) est évidemment plus courte que celle du second (5 octets). Mais comme tout ce qui précède ne concerne que des détails, la question du volume n’a pas été évoquée. D'autres problèmes de réduction de taille devraient être laissés au compilateur pour être résolus. Faites simplement un peu attention lors de l'écriture du code ASM intégré.