|
||||||||||||||||||||||||||||||||||||||||||||
|
Note : traduction par Stéphane Schreiber. Il est bien connu que pour réaliser des routines requérant une grande vitesse d'exécution, rien ne vaut l'assembleur. En fait, il serait plus juste de dire que quel que soit le langage utilisé, l'important est de bien l'utiliser. Un programmeur en langage machine averti, peut très rapidement distinguer trois types de programmes :
De l'excellent code se reconnaît très facilement ; il a tendance à utiliser presque tous les trucs connus, voire à en inventer de nouveaux. Sa qualité essentielle est... de faire ce pour quoi on l'a écrit ! Du bon code est plus facile à comprendre pour le débutant suivant pour ainsi dire à la lettre les règles connues de la programmation en assembleur. Il n'est malheureusement pas toujours optimisé, mais présente l'avantage de fonctionner. Reste le code "inqualifiable", qui est... heu... disons pas très joli à voir. La marche à suivre pour obtenir un code optimal peut-être divisée en trois étapes :
L'optimisation "ligne à ligne" Le jeu d'instructions du MC68000 n'est peut-être pas très vaste, mais ses nombreux modes d'adressage autorisent une programmation plus qu'optimisée, aussi bien en temps d'exécution qu'en taille de programme (par la suite, nous ne nous référons plus qu'au 68000 en oubliant les 68020 et 68030, car c'est à l'heure actuelle le processeur le plus répandu). Les premières règles à observer lors du choix des instructions sont :
L'exemple C est une parfaite illustration d'une règle amusante de l'assembleur, qui veut que deux instructions s'exécutent parfois plus rapidement qu'une seule, tout en prenant moins de place en mémoire ! Ce qui amène une septième règle importante : si vous pouvez utiliser MOVEQ pour des constantes 32 bits, faites-le ! Cette règle s'applique à toute instruction dans laquelle il est possible d'utiliser un registre de donnée annexe. Par exemple : Une autre astuce du même genre, lorsqu'on désire initialiser un registre avec une valeur trop grande pour l'instructeur MOVEQ : Pour initialiser des constantes comprises entre $80000000 à $01000000, les instructions ROL et ROR offrent une alernative interessante : Toutes les optimisations vues jusqu'ici ne s'appliquaient qu'aux registres de données, mais il existe des règles supplémentaires s'appliquant aux registres d'adresse. Utiliser la forme ".W" chaque fois que possible. Par exemple, pour charger une adresse constante dans a0, vous écririez peut-être : Sur Amiga, un problème courant avec les registres d'adresse, est la conversion de pointeurs BPTR en pointeurs APTR (NDLR : un pointeur de type BPTR vous vient tout droit du langage BCPL, avec lequel a été écrit une petite partie du système d'exploitation de l'Amiga, notamment quelques bibliothèques. Un pointeur de type APTR est quant à lui en provenance direct du langage C). Un pointeur BPTR se distingue par le fait qu'il est décalé de deux bits vers la droite, opération impossible sur un registre d'adresse. La première solution venant à l'esprit est donc : Il existe certainement d'autres optimisations possibles en assembleur 68000, mais ce sont là les plus courantes. La meilleure manière de les retenir, et même d'en apprendre d'autres, est encore d'avoir un livre officiel de chez Motorola indiquant pour chaque instruction le temps d'exécution. Quoiqu'il en soit, tout ceci ne représente qu'une petite partie de ce qu'il est possible de faire pour optimiser un programme entier. Il existe d'autre moyens que de bêtes substitutions d'instructions. L'optimisation locale Contrairement à tout ce que nous avons vu jusqu'ici, l'optimisation locale requiert un peu plus de réflexion quant aux moyens à utiliser. L'exemple le plus simple qui vienne à l'esprit est la multiplication. Ainsi, pour adresser un élément particulier d'un tableau, il est nécessaire de multiplier son indice par la taille maximale des éléments. En assembleur, un bon programmeur se débrouille toujours pour jongler avec les puissances de deux. Au lieu de : Il est important de noter que tous ces décalages "maison" ne s'occupent absolument pas d'un éventuel bit de signe, mais il est vrai qu'en assembleur, on ne s'en soucie que rarement. Si vous avez vraiment besoin du bit se signe, utilisez l'instruction EXT.L en lieu et place de MOVEQ=0. Une optimisation particulière concerne l'écriture d'un logiciel assembleur... en assembleur : lorsque l'on génère le code machine d'un registre quelconque, il faut d'abord masquer son numéro avec la constante 7, puis décaler ce résultat de 9 bits vers la gauche. Un bon programmeur aurait écrit quelque chose du style (en considérant que le numéro du registre se trouve dans d0) : Ne jamais oublier que certaines instructions affectent les bits du CCR Un bon programme en assembleur ne comporte pour ainsi dire aucune instruction TST. Le 68000 est en effet suffisamment bien conçu pour positionner les bits du CCR en fonction des opérations effectuées, tout seul comme un grand. Le seul cas ou le CCR n'est pas affecté est lors de l'initialisation d'un registre d'adresse. Mais chaque médaille à son revers : si vous modifiez après coup un programme pourtant considéré comme terminé, rien ne dit que vous n'allez pas modifier du même coup le CCR. C'est pourquoi il est parfois plus sûr d'utiliser l'instruction MOVEM, plus gourmande en mémoire et en temps d'exécution que MOVE, pour sauvegarder des registres, etc. Je ne vous apprendrais rien en vous disant que tester les bits du CCR permet de réagir à certaines conditions, pour programmer des branchements divers. L'assembleur offre le luxe de pouvoir tester trois conditions en une seule fois : La meilleure instruction du 68000 est encore DBcc (décrémentation d'un registre de données et branchement conditionnel). Même sur un 68010, toutes les instructions comprises entre DBcc et l'adresse du branchement sont copiées dans une mémoire cache interne, ce qui accélère leur traitement. Il faut toutefois porter une attention toute particulière à l'instruction DBRA :
Nous avons déjà fait un grand pas en avant, en économisant encore quelques octets et quelques cycles par-ci par-là. Il nous reste encore à optimiser le programme dans son ensemble. Choisissez vos registres avec beaucoup d'attention Un bon programme essaie de charger un ou plusieurs registres avec une valeur ou une adresse souvent utilisée, et n'y touche plus par la suite. Cette valeur est ainsi accessible en permanence. Le meilleur exemple sur l'Amiga est AbsExecBase, qui devrait être en permanence accessible dans le registre a6, quitte à le sauvegarder ou à le réinitialiser lors d'appels à d'autres bibliothèques qu'Exec. Évitez MOVEM autant que possible Il est souvent utile, lors d'appels à un sous-programme, de sauvegarder les registres corrompus pour les récupérer en revenant. Essayez plutôt de faire en sorte que vos routines n'utilisent pas les mêmes registres que le programme principal. Évitez LINK et UNLK autant que possible LINK est souvent utilisée dans les langages compilés, tel le C. On ne la croise par contre que rarement dans un programme en langage machine (NDLR : elle sert à créer un espace mémoire utilisable pour sauver des données trop grandes pour un registre. En pratique, on l'utilise surtout lors de la programmation de routines récursives). Quoiqu'il en soit, il est de loin préférable de créer une sous-routine supplémentaire chargée de sauver les valeurs, voire carrément de les empiler. Choisissez vos drapeaux avec attention Si un octet de mémoire vous sert de drapeau à trois états, il est plus efficient de lui affecter comme valeurs possibles -1, 0 et 1, ce qui permet des branchements plus rapides (avec Bcc, sans TST ni CMP avant). Utiliser les tableaux comme il faut ! Beaucoup d'opérations peuvent être réalisées à l'aide de tableaux. Par exemple, pour une rotation de bloc dans un programme de dessin, il est préférable de calculer auparavant les 360 valeurs du sinus et de le stocker dans un tableau, plutôt que de les calculer par programmation. L'artillerie lourde Tout ceci ne représente en aucun cas une liste exhaustive et immuable de ce qu'il faut faire pour améliorer ses programmes en assembleur 68000. Avec un peu de pratique, tout ce qui a été dit ici deviendra partie intégrante de vous-même, comme un réflexe de programmation. En moyenne, seulement 5% d'un programme monopolisant quelques 95% de son temps d'exécution. Il y a beaucoup à gagner en s'occupant d'optimiser les 95% restants... Dernier truc Apprenez à utiliser le bit X Le bit X du CCR est une des choses les plus étranges du 68000. Il n'est influencé que par peu d'instructions, et n'est de fait presque jamais utilisé. Il possède pourtant la très intéressante particularité d'initialiser un registre à 0 ou à -1, d'après sa valeur initiale : subx.1 d0,d0. Voilà. Nous allons terminer avec un petit test, histoire de voir si vous feriez un bon "optimiseur" : une valeur quelconque est dans d0, et l'on ne connaît pas l'état du CCr. Initialisez d1 à 0 si d0 est nul, et à 1 dans le cas contraire. Vous pouvez au besoin perdre le contenu de d0. Solution :
|