Utiliser le langage D'assemblage en C / C++

je me souviens avoir lu quelque part que pour vraiment optimiser et accélérer certaines sections du code, les programmeurs écrivent cette section en langage Assembleur. Mes questions sont -

  1. cette pratique est-elle encore en vigueur? et Comment fait-on cela?
  2. n'est-ce pas un peu trop encombrant et archaïque d'écrire en langage assemblé?
  3. quand nous compilons du code C (avec ou sans-O3 flag), le compilateur fait de l'optimisation du code et lie tous libraries & converts le code en fichier d'objet binaire. Alors, quand on lance le programme, il est déjà dans sa forme la plus basique, c'est à dire binaire. Comment le fait d'induire un "langage D'Assemblée" aide-t-il?

j'essaie de comprendre ce concept & toute aide ou liens est très apprécié.

mise à jour: reformuler le point 3 comme demandé par dbemerlin - parce que vous pourriez être en mesure d'écrire un code d'assemblage plus efficace que le compilateur génère mais si vous n'êtes pas un expert en assemblage, votre code fonctionnera plus lentement que la plupart des humains, car souvent le compilateur optimise le code mieux que la plupart des humains.

40
demandé sur Ryan Tenney 2010-11-17 11:33:59

12 réponses

la seule fois où il est utile de revenir au langage de l'assemblée est quand

  • les instructions CPU n'ont pas d'équivalents fonctionnels en C++ (par exemple, instruction simple-instructions à données multiples, opérations BCD ou arithmétiques décimales)

    ou

  • pour certains raison inexplicable - l'optimiseur ne parvient pas à utiliser les meilleures instructions CPU

...ET...

  • l'utilisation de ces instructions CPU donnerait un coup de pouce significatif et utile de performance au Code goulot.

simplement en utilisant l'assemblage en ligne pour faire une opération qui peut facilement être exprimée en C++ - comme l'ajout de deux valeurs ou la recherche dans une chaîne de caractères - est activement contre-productif, parce que:

  • le compilateur sait faire cela aussi bien
    • pour vérifier cela, regardez sa sortie d'assemblage (par exemple gcc -S ) ou démontez le code de la machine
  • vous restreignez artificiellement ses choix en ce qui concerne l'allocation de registre, les instructions CPU, etc., de sorte qu'il peut prendre plus de temps pour préparer les registres CPU avec les valeurs nécessaires pour exécuter votre instruction codée en dur, puis plus longtemps pour revenir à une allocation optimale pour les instructions futures
    • les optimiseurs de compilateurs peuvent choisir entre des instructions de performance équivalentes spécifiant différents registres pour réduire au minimum la copie entre eux, et peuvent choisir des registres de telle sorte qu'un seul noyau puisse traiter plusieurs instructions au cours d'un cycle, tandis que forcer tout le monde à travers des registres spécifiques le sérialiserait
      • en toute équité, GCC a des façons d'exprimer les besoins pour des types spécifiques de registres sans contraindre le CPU à un registre exact, tout en permettant de telles optimisations, mais c'est le seul assemblage en ligne que j'ai jamais vu qui s'adresse à ce
  • si un nouveau modèle CPU sort l'année prochaine avec une autre instruction qui est 1000% plus rapide pour cette même opération logique, alors le vendeur de compilateur est plus susceptible de mettre à jour leur compilateur pour utiliser que l'instruction, et donc votre programme de bénéficier une fois recompilé, que vous êtes (ou celui qui maintient le logiciel est alors)
  • le compilateur sélectionnera une approche optimale pour l'architecture cible dont il parle: si vous codez dur une solution alors il aura besoin d'être un plus petit-commun-dénominateur ou #ifdef - ed pour vos plates-formes
  • le langage d'assemblage n'est pas aussi portable que C++, à la fois à travers les CPU et les compilateurs, et même si vous port une instruction, il est possible de faire une erreur sur les registres qui sont sûrs de Clabber, les conventions de passage d'argument etc.
  • d'autres programmeurs peuvent ne pas savoir ou ne pas être à l'aise avec l'assemblage

un point de vue que je pense qu'il vaut la peine de garder à l'esprit est que lorsque C a été introduit, il a dû gagner sur beaucoup de hardcore assembly language programmeurs qui se sont agitées sur le code machine généré. Les Machines avaient moins de puissance CPU et RAM à l'époque et tu peux parier que les gens se sont agités pour la plus petite chose. Les optimiseurs sont devenus très sophistiqués et ont continué à s'améliorer, tandis que les langages d'assemblage des processeurs comme le x86 sont devenus de plus en plus compliqués, tout comme leurs pipelines d'exécution, leurs caches et d'autres facteurs impliqués dans leurs performances. Vous ne pouvez plus simplement ajouter des valeurs à partir d'un tableau de cycles-par-instruction. Les rédacteurs de compilateurs passent du temps à considérer tous ces facteurs subtils (en particulier ceux qui travaillent pour CPU). les fabricants, mais cela augmente la pression sur les autres compilateurs aussi). Il est maintenant impossible pour les programmeurs d'assemblage de faire la moyenne - sur n'importe quelle application non triviale-d'une efficacité de code nettement meilleure que celle générée par un bon compilateur d'optimisation, et ils sont très susceptibles de faire pire. Ainsi, l'utilisation de l'assemblage devrait être limitée aux moments où il fait vraiment une différence mesurable et utile, valant les coûts d'assemblage et de maintenance.

26
répondu Tony Delroy 2015-06-25 03:39:13

tout d'Abord, vous devez établir le profil de votre programme. Ensuite, vous optimisez les chemins Les plus utilisés en code C ou c++. à moins que les avantages soient clairs, vous ne réécrivez pas dans l'assembleur . L'utilisation de assembleur rend votre code plus difficile à entretenir et beaucoup moins portable - il n'en vaut pas la peine, sauf dans des situations très rares.

14
répondu sharptooth 2017-05-23 12:10:36

(1) Oui, la façon la plus simple d'essayer ceci est d'utiliser l'assemblage en ligne, cela dépend du compilateur mais ressemble habituellement à quelque chose comme ceci:

__asm
{
    mov eax, ebx
}

(2) C'est très subjectif

(3) parce que vous pourriez être en mesure d'écrire un code d'assemblage plus efficace que celui généré par le compilateur.

10
répondu Andreas Brinck 2010-11-17 09:05:20

il y a très peu de raisons d'utiliser le langage assembleur de nos jours, même les constructions de bas niveau comme le SSE et le MMX plus ancien ont des intrinsèques intégrés dans les deux gcc et MSVC (icc too I bet but I never used it).

honnêtement, les optimiseurs de nos jours sont tellement agressifs que la plupart des gens ne pouvaient même pas égaler la moitié de leur code d'écriture de performance en assemblage. Vous pouvez changer la façon dont les données sont ordonnées en mémoire (pour la localité) ou en dire plus au compilateur sur votre code (par #pragma ), mais en fait en écrivant le code d'assemblage... je doute que vous en retiriez quelque chose.

@VJo, notez que l'utilisation d'intrinsèques dans le code C de haut niveau vous permettrait de faire les mêmes optimisations, sans utiliser une seule instruction d'assemblage.

et pour ce que ça vaut, il y a eu des discussions sur le prochain compilateur C++ de Microsoft, et comment ils vont faire tomber l'assemblage en ligne de celui-ci. Qui parle des volumes au sujet de la nécessité pour elle.

4
répondu Blindy 2010-11-17 08:45:39

vous devriez lire le livre classique Zen of Code Optimization et le suivi Zen of Graphics Programming par Michael Abrash .

sommairement dans le premier livre, il a expliqué comment utiliser la programmation d'assemblage poussé aux limites. Dans le suivi, il explique que les programmeurs devraient utiliser un langage de niveau supérieur comme C et essayer seulement d'optimiser certains points très spécifiques en utilisant l'assemblage, si nécessaire à tous.

Une motivation de ce changement de l'esprit était qu'il a vu que les programmes hautement optimisés pour une génération de processeur pourraient devenir (quelque peu) lent dans la prochaine génération de la même famille de processeur par rapport au code compilé à partir d'un langage de haut niveau (maube compiler en utilisant de nouvelles instructions par exemple).

une autre raison est que les compilateurs sont assez bons et optimiser agressivement aujourd'hui, il ya généralement beaucoup plus de performance à gagner de travailler sur les algorithmes qui convertissent le code C à l'assemblage. Même pour Programmation GPU (Graphic Cards processors) vous pouvez le faire en utilisant C à l'aide de cuda ou OpenCL.

il y a encore quelques cas (rares) où vous devez utiliser l'assemblage, généralement pour obtenir un contrôle très fin sur le matériel. Mais même dans le code du noyau du système D'exploitation, il s'agit généralement de très petites parties et peu de code.

3
répondu kriss 2010-11-17 09:04:51

Je ne pense pas que vous ayez spécifié le processeur. Réponses différentes selon le processeur et l'environnement. Manière générale, la réponse est oui, il est encore fait, il n'est pas archaïque certainement. La raison générale est que les compilateurs, parfois ils font un bon travail à l'optimisation en général, mais pas vraiment bien pour des objectifs spécifiques. Certains sont vraiment bons à une cible et pas si bons à d'autres. La plupart du temps, il est assez bon, la plupart du temps vous voulez code C portable et non pas non-portable assembleur. Mais vous constatez toujours que les bibliothèques C vont encore optimiser memcpy et d'autres routines que le compilateur ne peut tout simplement pas comprendre qu'il existe un moyen très rapide de l'implémenter. En partie parce que ce cas de coin n'est pas la peine de passer du temps à faire optimiser le compilateur pour, il suffit de le résoudre en assembleur et le système de construction a beaucoup de si cette cible puis utiliser C si cette cible utiliser C si cette cible utiliser asm, si cette cible utiliser asm. Donc ça se produit toujours, et je pense que ça doit continuer pour toujours. dans certains domaines.

X86 est propre bête avec beaucoup d'histoire, nous sommes à un point où vous ne pouvez vraiment pas pratique d'écrire une goutte d'assembleur qui est toujours plus rapide, vous pouvez certainement optimiser les routines d'un processeur spécifique sur une machine spécifique sur un jour précis, et à effectuer le compilateur. Autres que pour certains cas spécifiques, il est généralement inutile. Éducatif mais dans l'ensemble ne vaut pas le temps. Notez également que le processeur n'est plus le goulot d'étranglement, donc un le compilateur C Générique bâclé est assez bon, trouver la performance ailleurs.

autres plates-formes qui désignent souvent embedded, arm, mips, avr, msp430, pic, etc. Vous pouvez ou non exécuter un système d'exploitation, vous pouvez ou non exécuter avec un cache ou d'autres choses semblables que votre bureau a. Donc les faiblesses du compilateur le montreront. Notez également que les langages de programmation continuent d'évoluer loin des processeurs plutôt que vers eux. Même dans le cas de C considéré peut-être comme une langue de bas niveau, il ne correspond pas à l'ensemble d'instruction. Il y aura toujours des moments où vous pourrez produire des segments d'assembleur qui surpassent le compilateur. Pas nécessairement le segment qui est votre goulot d'étranglement, mais dans l'ensemble du programme, vous pouvez souvent apporter des améliorations ici et là. Tu dois encore vérifier la valeur de faire ça. Dans un environnement embarqué, il peut faire la différence entre le succès et l'échec d'un produit. Si votre produit a $25 par unité investie dans plus de puissance faim, immobilier de bord, processeurs à grande vitesse donc vous n'avez pas à utiliser assembleur, mais votre concurrent dépense 10 $ou moins par unité et est prêt à mélanger asm avec C pour utiliser des mémoires plus petites, utiliser moins de puissance, des pièces moins chères, etc. Eh bien, tant que le NRE est récupéré, alors la solution mélangée avec asm sera à long terme.

Vrai embedded est un marché spécialisé avec des ingénieurs spécialisés. Un autre marché intégré, votre linux intégré roku, tivo, etc. Les téléphones intégrés, etc tous ont besoin d'avoir des systèmes d'exploitation portables pour survivre parce que vous avez besoin de développeurs tiers. La plate-forme doit donc ressembler davantage à un bureau qu'à un système intégré. Enfoui dans la bibliothèque C comme mentionné ou le système d'exploitation il peut y avoir quelques optimisations assembleur, mais comme avec le bureau vous voulez essayer de jeter plus de matériel pour que le logiciel puisse être portable au lieu de la main optimisée. Et votre ligne de produits ou système d'exploitation intégré va échouer si l'assembleur est nécessaire pour le succès de tiers.

la plus grande préoccupation que j'ai est que ces connaissances sont perdues à un rythme alarmant. Parce que personne ne inspecte l'assembleur, parce que personne n'écrit en assembleur, etc. Personne ne remarque que les compilateurs ne se sont pas améliorés en ce qui concerne le code produit. Les développeurs pensent souvent qu'ils doivent acheter plus de matériel au lieu de se rendre compte qu'en connaissant le compilateur ou la façon de mieux programmer ils peuvent améliorer leurs performances de 5 à plusieurs centaines de pour cent avec le même compilateur, parfois avec le même code source. 5-10% généralement avec le même code source et compilateur. gcc 4 ne produit pas toujours un meilleur code que gcc 3, je garde les deux autour parce que parfois gcc3 fait mieux. Les compilateurs cibles spécifiques peuvent (pas toujours faire) exécuter des cercles autour de gcc, vous pouvez voir une amélioration de quelques centaines de pour cent parfois avec le même code source compilateur différent. D'où tout cela vient-il? Le les gens qui prennent encore la peine de regarder et / ou d'utiliser assembleur. Certains de ces gens travaillent sur le compilateur backends. L'avant et le milieu sont amusants et éducatifs certes, mais l'arrière-plan est où vous faites ou cassez la qualité et la performance du programme résultant. Même si vous n'écrivez jamais assembleur mais regardez seulement la sortie du compilateur de temps en temps (gcc-O2-syprog.c) il fera de vous un meilleur niveau élevé programmeur et conservent certaines de ces connaissances. Si personne n'est disposé à connaître et écrire assembleur alors par définition, nous avons abandonné dans l'écriture et la maintenance des compilateurs pour les langues de haut niveau et les logiciels en général cesseront d'exister.

Comprendre qu'avec gcc par exemple la sortie du compilateur est l'assemblée qui est transmis à un assembleur qui le transforme en code objet. Le compilateur C ne produit normalement pas de binaires. Les objets une fois combinés dans le binaire final, sont faits par le linker, encore un autre programme qui est appelé par le compilateur et ne fait pas partie du compilateur. Le compilateur transforme C ou C++ ou ADA ou n'importe quoi en assembleur puis l'assembleur et les outils de linker prennent le reste du chemin. Les recompileurs dynamiques, comme tcc par exemple, doivent être capables de générer des binaires à la volée d'une manière ou d'une autre, mais je vois cela comme l'exception et non la règle. LLVM a sa propre solution d'exécution ainsi que très visiblement montrant le haut niveau de code interne pour cibler le code sur le chemin binaire si vous l'utilisez comme un compilateur croisé.

donc retour au point, oui, il est fait, plus souvent que vous le pensez. La plupart du temps a à voir avec la langue ne comparant pas directement à l'ensemble d'instruction, et puis le compilateur ne produit pas toujours assez rapidement le code. Si vous pouvez obtenir par exemple des douzaines de fois l'amélioration sur les fonctions fortement utilisées comme malloc ou memcpy. Ou si vous voulez avoir un lecteur vidéo HD sur votre téléphone sans support matériel, équilibrez les avantages et les inconvénients de l'assembleur. Les marchés véritablement intégrés utilisent encore assez assembleur, parfois, c'est tout C mais parfois, le logiciel est entièrement codé en assembleur. Pour pc x86, le processeur n'est pas le goulot d'étranglement. Les processeurs sont microcoded. Même si vous faites belle apparence assembleur sur la surface il ne fonctionnera pas vraiment vite sur toutes les familles de processeurs x86, négligée, assez bon code est plus susceptible d'exécuter à peu près le même dans l'ensemble.

je recommande fortement l'assembleur d'apprentissage pour les ISAs non-x86 comme arm, thumb/thumb2, mips, msp430, avr. Les cibles qui ont des compilateurs, en particulier ceux avec le soutien de compilateur gcc ou llvm. Apprenez l'assembleur, apprenez à comprendre la sortie du compilateur C, et prouvez que vous pouvez faire mieux en modifiant réellement cette sortie et en la testant. Cette connaissance aidera à rendre votre code de haut niveau de bureau beaucoup mieux sans assembleur, plus rapide et plus fiable.

3
répondu old_timer 2010-11-17 20:06:46

regardez ici , où le type a amélioré ses performances 6 fois en utilisant le code d'assemblage. Donc, la réponse est : c'est encore fait, mais le compilateur fait du bon travail.

2
répondu BЈовић 2010-11-17 08:38:49

ça dépend. Il est (encore) fait, dans certaines situations, mais pour la plupart, c'est pas la peine. Les CPU modernes sont incroyablement complexes, et il est tout aussi complexe d'écrire un code d'assemblage efficace pour eux. Ainsi, la plupart du temps, l'assemblée vous écrivez à la main de finir plus lente que ce que le compilateur peut générer pour vous.

en supposant qu'un compilateur décent est sorti au cours des dernières années, vous pouvez généralement modifier votre code C / C++ pour obtenir la même performance avantage que vous utiliseriez l'assemblage.

beaucoup de gens dans les commentaires et les réponses ici parlent de la" N times speedup " ils ont obtenu réécrire quelque chose en assemblée, mais que par lui-même ne signifie pas trop. J'ai obtenu une accélération de 13 fois de réécrire une fonction C évaluant les équations de dynamique des fluides dans C , en appliquant beaucoup des mêmes optimisations que vous le feriez si vous deviez l'écrire dans l'assemblage, en connaissant le matériel, et par le profilage. À la fin, il s'est rapproché assez près de la performance de pointe théorique du CPU qu'il y aurait no point dans la réécriture en assemblage. Habituellement, ce n'est pas le langage qui est le facteur limitant, mais le code que vous avez écrit. Aussi longtemps que vous n'utilisez pas d'instructions "spéciales" avec lesquelles le compilateur a des difficultés, il est difficile de battre le code C++ bien écrit.

L'assemblage

n'est pas magiquement plus rapide. Ça enlève juste le compilateur de la boucle. Que est souvent une mauvaise chose, à moins que vous vraiment savez ce que vous faites, puisque le compilateur effectue beaucoup d'optimisations qui sont vraiment très douloureux à faire manuellement. Mais dans de rares cas, le compilateur ne comprend tout simplement pas votre code, et ne peut pas générer un assemblage efficace pour lui , et puis , il pourrait être utile d'écrire un assemblage vous-même. Autre que le développement de pilote ou similaire (où vous devez manipuler le matériel directement), le seul lieu je peux penser à où l'écriture assemblée peut être la peine il est si vous êtes coincé avec un compilateur qui ne peut pas générer de code SSE efficace à partir d'intrinsèques (tels que MSVC). Même là, je commencerais à utiliser intrinsics en C++, et je le profilerais et essaierais de le modifier autant que possible, mais comme le compilateur n'est tout simplement pas très bon pour cela, il pourrait éventuellement être utile de réécrire ce code dans assembly.

2
répondu jalf 2010-11-17 12:21:39

sur mon travail, j'ai utilisé l'assemblage sur cible embarquée (micro-contrôleur) pour un accès de bas niveau.

mais pour un logiciel PC, Je ne pense pas qu'il soit très utile.

1
répondu Benoît 2010-11-17 08:38:33

j'ai un exemple d'optimisation d'assemblage que j'ai fait, mais encore une fois c'est sur une cible intégrée. Vous pouvez voir quelques exemples de programmation d'assemblage pour PC aussi, et il crée des programmes vraiment petits et rapides, mais généralement pas la peine de l'effort (cherchez "assembler pour windows", vous pouvez trouver quelques très petits et jolis programmes).

mon exemple était quand j'écrivais un contrôleur d'imprimante, et il y avait une fonction qui était censée être appelée Toutes les 50 micro-secondes. Il faut qu'il refasse des morceaux, plus ou moins. En utilisant C, j'ai pu le faire en environ 35microsecondes, et avec l'assemblage, je l'ai fait en environ 8 microsecondes. C'est une procédure très spécifique, mais quand même, quelque chose de réel et nécessaire.

1
répondu SurDin 2010-11-17 08:47:16

sur certains appareils embarqués (téléphones et PDA), c'est utile car les compilateurs ne sont pas terriblement matures, et peuvent générer du code extrêmement lent et même incorrect. J'ai personnellement dû contourner, ou écrire du code d'assemblage pour corriger, la sortie buggée de plusieurs compilateurs différents pour les plateformes embarquées basées sur ARM.

1
répondu Graham Borland 2010-11-17 16:50:07
  1. Oui. Utilisez les modules d'assemblage d'objets inline ou link. La méthode à utiliser dépend de la quantité de code d'assemblage que vous devez écrire. Habituellement, il est correct d'utiliser l'assemblage en ligne pour un couple de lignes et de passer à des modules d'objet séparés une fois si c'est plus d'une fonction.
  2. certainement, mais parfois c'est nécessaire. L'exemple proéminent ici serait la programmation d'un système d'exploitation.
  3. la plupart des compilateurs aujourd'hui optimisez le code que vous écrivez dans un langage de haut niveau bien mieux que quiconque pourrait écrire du code d'assemblage. Les gens l'utilisent principalement pour écrire du code qui serait autrement impossible d'écrire dans un langage de haut niveau comme C. Si quelqu'un l'utilise pour quelque chose d'autre signifie qu'il est soit meilleur à l'optimisation qu'un compilateur moderne (je doute que) ou tout simplement stupide, par exemple il ne sait pas ce que les drapeaux de compilateur ou les attributs de fonction à utiliser.
0
répondu flacs 2010-11-17 09:03:57