Quand les compilateurs ont-ils commencé à générer du code optimisé qui s'exécute plus rapidement que le code d'assemblage d'un programmeur moyen?

Sep 12 2020

Il est fortement déconseillé d'écrire votre propre code en assembly maintenant car, dans la plupart des cas, il gcc -O3fait de la magie. Mais dans les années 80, on croyait que le code C compilé prend 4 (?) Fois ou plus qu'un équivalent d'assemblage bien organisé. Quand et pourquoi coder C pour la performance comme premier choix devient-il la pratique reçue? Quel compilateur l'a créé en premier, sur quelle architecture?

Existe-t-il des compilateurs de langage de haut niveau (Ada / COBOL / Fortran / Pascal) autres que les familles C qui génèrent un code optimisé surpassant les programmeurs d'assemblage moyens?

Réponses

33 CecilWard Sep 14 2020 at 02:46

En tant qu'ancien programmeur en langage assembleur professionnel, je dirais qu'à la fin des années 1980, un certain nombre de compilateurs C étaient devenus disponibles dont la sortie était aussi bonne que ce qu'un programmeur assembleur expérimenté pouvait produire. J'ai utilisé divers compilateurs x86 C à l'époque et JPI C et WATCOM C en 1988 et MSVC 1.0 en 1994 ont produit une sortie aussi bonne que tout ce que je pouvais écrire et m'a même appris l'astuce occasionnelle.

29 Chromatix Sep 12 2020 at 16:43

Pour commencer, il est bien connu que FORTRAN II pour la série IBM 1401 a été spécialement conçu pour générer un code objet de qualité suffisamment élevée pour rendre inutile la programmation d'assemblage de routines numériques. Les compilateurs FORTRAN ont en grande partie conservé cet héritage depuis.

La qualité des compilateurs C a toujours été très variable. Il faut se rappeler que C a été conçu à l'origine comme une sorte de "langage d'assemblage portable" avec une correspondance raisonnable avec les instructions et les modes d'adressage du PDP-11. Un C correctement écrit avec même un simple compilateur pourrait être remarquablement efficace. Mais le code objet produit par certains des premiers compilateurs, en particulier pour les plates-formes micro-informatiques telles que le PC, était déraisonnablement mauvais.

Aujourd'hui, même avec les compilateurs sophistiqués maintenant disponibles, il est toujours possible pour un codeur d'assemblage expérimenté d'écrire un meilleur code qu'un compilateur ne produit. Ils peuvent utiliser des instructions que le compilateur ne sait pas utiliser, ou comprendre les algorithmes plus profondément que ce qui peut être exprimé en C. Au minimum, ils peuvent commencer avec la sortie d'un compilateur et s'améliorer à partir de là.

L'avantage du compilateur est qu'il génère du code plus rapidement , c'est à dire. avec moins d'effort du développeur, et le code source est plus facile à maintenir. Les compilateurs sophistiqués d'aujourd'hui aident à réduire le déficit de performance qui l'accompagnait traditionnellement. Mais les compilateurs sophistiqués ne sont pas nouveaux.

16 MarkMorganLloyd Sep 13 2020 at 04:56

Je suis tombé sur un commentaire intéressant il y a quelques jours que Donald Knuth a été profondément impressionné quand il a découvert que 5 * 5 - 25 était optimisé par un compilateur (ALGOL?) Pour un registre clair. Cela aurait été à la fin des années 1950.

Le livre de Frances Allen sur l'optimisation a été publié en 1972. Je conviens que de nombreux compilateurs PC des années 1980 produisaient un code médiocre, mais ils se distinguaient également par (a) être bon marché et (b) ne pas supposer la disponibilité d'une quantité arbitrairement élevée de mémoire qui leur aurait permis d'optimiser une expression arbitrairement complexe (sans parler de tenter d'optimiser à travers les expressions).

Je noterais également avoir entendu un commentaire à la fin des années 80 selon lequel certains des compilateurs les plus efficaces étaient pour Modula-2, car le langage source donnait au compilateur des indices suffisants sur ce qui était attendu. Les compilateurs Topspeed (écrits en grande partie par George Barwood) étaient plutôt bons.

Je pense donc qu'une réponse raisonnable serait qu'en principe un compilateur pourrait se rapprocher de l'efficacité d'un programmeur humain du début au milieu des années 1970, à condition que l'utilisateur paye suffisamment pour le compilateur et à condition que l'hôte sur lequel il s'exécute dispose de ressources suffisantes.

16 mcleod_ideafix Sep 14 2020 at 05:40

Les compilateurs ont commencé à générer un code plus efficace que le programmeur assembleur moyen au moment où les architectures sont devenues si complexes que le programmeur assembleur n'a pas été en mesure d'en gérer tous les détails. Des choses comme quelle instruction doit aller au tuyau U ou au tuyau V dans un processeur Pentium, etc.

Lequel était le premier? Je dirais que pour l'architecture x86, c'était le compilateur Intel. Au moins, c'était le premier (ttbomk) qui était capable de détecter des boucles candidates pour la vectorisation et d'utiliser les instructions MMX, SSE et AVX avec elles.

Cependant, le compilateur Watcom C avait la réputation de générer un code machine de très bonne qualité dans les jours précédant Pentium et même 486. Certaines des options d'optimisation que j'ai vues par la suite dans le compilateur Intel étaient déjà disponibles dans le Watcom.

10 supercat Sep 14 2020 at 05:41

Le principal avantage que C aurait par rapport à un programmeur en langage assembleur est la possibilité d'ajuster le code généré pour gérer les changements de différentes constantes. Lors de l'utilisation d'un compilateur de qualité, si l'on écrit "unsigned foo = bar / 8;" un compilateur peut générer une instruction de décalage, mais si la constante devait plus tard être 5, un compilateur peut utiliser d'autres moyens pour effectuer le calcul, comme une combinaison de multiplication et de décalage. Si, en revanche, le code avait été écrit dans un langage d'assemblage optimal, la modification du diviseur nécessiterait des modifications plus substantielles du code.

Sinon, alors que les fabricants de certains compilateurs gratuits voudront peut-être prétendre que leurs compilateurs peuvent faire aussi bien ou mieux que les langages d'assemblage, et bien qu'ils puissent trouver des optimisations "intelligentes" qui leur permettent parfois de le faire, ils génèrent régulièrement du code qui est bien pire que ce à quoi on pourrait s'attendre de tout programmeur en langage assembleur compétent. Par exemple, en ciblant le microcontrôleur Cortex-M0 populaire, gcc traitera l'utilisation d'une constante dans une boucle en générant un code qui recharge la constante à chaque fois dans la boucle. Même si la constante est chargée dans un objet qualifié par registre avant la boucle, gcc reportera toujours la charge jusqu'à ce que la valeur soit utilisée, et réexécutera la charge à chaque itération de la boucle.

7 LorenPechtel Sep 13 2020 at 09:41

Il y a un autre facteur qui se passe ici, aussi, que j'ai remarqué en examinant la sortie du compilateur par rapport à ce que j'aurais écrit (certes, je n'ai pas fait assez d'assemblage pour en être un véritable expert):

Compte tenu de ce que savent les compilateurs, j'ai été impressionné par l'efficacité de son codage. Cependant, dans tous les cas que j'ai examinés, j'aurais pu faire mieux parce que je savais des choses sur le problème que le compilateur ne connaissait pas.

4 skamradt Sep 16 2020 at 04:35

C'est vraiment un problème de coût / bénéfice. L'assemblage optimisé à la main pourrait encore être plus rapide que votre optimisation pour un chemin de code spécifique, pas un chemin plus général. Cela étant dit, chaque itération d'un compilateur pourrait prendre de meilleures décisions et générer un code plus serré avec moins de place pour une optimisation supplémentaire. À un moment donné, les quelques instructions supplémentaires qui pourraient être enregistrées ne valent pas le temps / coût à optimiser. Il fut un temps, je crois au début des années 90, où nous utilisions l'assemblage partiel. Certaines routines étaient un assemblage optimisé à la main pour des performances critiques, mais la plupart ont été effectuées dans des langages de niveau supérieur. Finalement, ces routines d'assemblage optimisées à la main ont été recodées dans des langages de niveau supérieur à mesure que les puces devenaient plus rapides et que le besoin de gains de performances était réduit.

Il y a quelques années à peine, j'ai dépoussiéré ma casquette et mes robes de sorciers et codé à la main une minuscule routine ASM en ligne pour effectuer une transformation simple ... davantage parce que je pouvais raser quelques tics d'une routine qui était appelée de manière serrée. boucle et pourrait gérer les registres moi-même. Le résultat final était quelque chose qui a exécuté une routine C codée de manière similaire d'environ deux fois (bien que nous parlions de tics). Il est possible qu'une future version du compilateur puisse générer un code plus strict et / ou de nouvelles technologies de processeur réduiraient davantage les gains notables.

2 BillFerguson Sep 15 2020 at 07:36

Mon moment eureka était à la fin des années 80 (88 ou 89) lorsqu'un développeur senior de notre équipe a décidé qu'une routine de langage d'assemblage codée à la main qu'il avait besoin de changer devait d'abord être réécrite en C. À cette époque, nous utilisions le compilateur WATCOM C. Le résultat immédiat était que la version C compilée était 20% plus petite. Je ne me souviens plus de la différence de vitesse.

Ce jour-là, j'ai envoyé un e-mail au meilleur développeur de WATCOM sur le compilateur C rapportant le résultat, et j'ai affirmé que je n'écrirais jamais une autre routine en langage assembleur. Je ne l'ai toujours pas fait, bien qu'avec la montée en puissance d'Arduino et de minuscules microprocesseurs, je ne l'exclurais plus.

1 PatrickSchlüter Sep 14 2020 at 19:23

Jamais. C'est ma réponse courte et provocante. Le code généré par le compilateur a été choisi par un programmeur, les optimisations appliquées peuvent également être appliquées à l'assemblage, donnant un temps et des ressources illimités au programmeur, il pourra toujours générer un meilleur code que le compilateur. La question est de savoir si cela vaut la peine d'essayer de surmonter les limitations du compilateur ou non. Il y a une limite qu'un compilateur ne peut pas briser qu'un humain peut. Le compilateur doit se conformer à certaines contraintes (ABI, UB, conventions d'appel, utilisation des registres, etc.) que l'humain peut décider de violer.

1 KRKOwner Sep 28 2020 at 06:45

Je suppose que la différence entre "un programmeur moyen" et un compilateur est que le compilateur a une "sympathie mécanique" avec le matériel sur lequel il est compilé. Ressentez également le besoin de citer Donald Knuth / Hoare / Dijkstra, selon à qui vous demandez: "l'optimisation prématurée est la racine de tout mal".
Dans le monde actuel du cloud computing, tout devient flou: les machines virtuelles, les conteneurs et les machines virtuelles d'exécution (par exemple la machine virtuelle Java) peuvent tous coexister ensemble. Par conséquent, les micro-optimisations du compilateur n'ont pas de sens dans le schéma plus grand des choses - le code optimisé pour un conteneur peut ne pas être pertinent sur la VM / le matériel physique sur lequel il s'exécute.
Bien sûr, si nous parlons de contrôle nu, alors c'est important. Cependant, ces scénarios sont assez niches, à moins que nous ne parlions d'exécuter du code sur des micro-contrôleurs, l'optimisation de la puissance en optimisant les cycles du processeur est une bonne chose. x le nombre de cycles du processeur coûte des microampères de durée de vie de la batterie, ce qui peut être critique pour certaines applications.