Instruction LEA ou ADD?

Quand je suis en écriture manuscrite, je choisis généralement le formulaire

lea eax, [eax+4] 

Sur la forme ..

 add eax, 4 

J’ai entendu dire que lea est une instruction “0-clock” (comme NOP), alors que “add” ne l’est pas. Cependant, lorsque je regarde l’assemblage produit par le compilateur, je vois souvent la dernière forme utilisée au lieu du premier. Je suis assez intelligent pour faire confiance au compilateur, alors est-ce que quelqu’un peut faire la lumière sur celui qui est le meilleur? Lequel est le plus rapide? Pourquoi le compilateur choisit-il la dernière forme sur la première?

Une différence significative entre LEA et ADD sur les processeurs x86 est l’unité d’exécution qui exécute réellement l’instruction. Les processeurs x86 modernes sont superscalaires et disposent de plusieurs unités d’exécution fonctionnant en parallèle, le pipeline les alimentant un peu comme un round-robin (les bloqueurs de barres). En d’autres termes, la LEA est traitée par l’une des unités chargées de l’adressage (ce qui se produit à un stade précoce du pipeline), tandis que la fonction ADD est ADD à la ou aux ALU (unité arithmétique / logique) et tardive. le pipeline. Cela signifie qu’un processeur x86 superscalaire peut exécuter simultanément un LEA et une instruction arithmétique / logique.

Le fait que LEA passe par la logique de génération d’adresses au lieu des unités arithmétiques est également la raison pour laquelle on l’appelait «horloges zéro»; Cela ne prend pas de temps pour s’exécuter, car la génération d’adresse a déjà eu lieu au moment où elle est / est exécutée.

Ce n’est pas gratuit , car la génération d’adresses est une étape dans le pipeline d’exécution, mais elle ne comporte aucune surcharge d’exécution. Et il n’occupe pas un slot dans le ou les pipeline (s) ALU.

Edit: Pour clarifier, LEA n’est pas gratuit . Même sur les CPU qui ne l’implémentent pas via l’unité arithmétique, l’exécution prend du temps en raison des instructions de décodage / envoi / retrait et / ou des autres étapes du pipeline traversées par toutes les instructions. Le temps nécessaire à l’exécution de LEA se produit uniquement à un stade différent du pipeline pour les processeurs qui l’implémentent via la génération d’adresses.

Je suis assez intelligent pour faire confiance au compilateur, alors est-ce que quelqu’un peut faire la lumière sur celui qui est le meilleur?

Oui un peu. Tout d’abord, je prends ceci du message suivant: https://groups.google.com/group/bsdnt-devel/msg/23a48bb18571b9a6

Dans ce message, un développeur optimise un assemblage que j’ai écrit très mal pour fonctionner rapidement dans les processeurs Intel Core 2. Dans le cadre de ce projet, il s’agit d’une bibliothèque bsd bignum dans laquelle quelques développeurs et moi-même avons été impliqués.

Dans ce cas, tout ce qui est optimisé est l’ajout de deux tableaux qui ressemblent à ceci: uint64_t* x, uint64_t* y . Chaque “membre” ou membre du tableau représente une partie du bignum; le processus de base consiste à itérer à partir du membre le moins significatif, append la paire et continuer vers le haut, en passant le report (tout dépassement) à chaque fois. adc fait pour vous sur un processeur (il n’est pas possible d’accéder à l’indicateur de portage de CI).

Dans ce morceau de code, une combinaison de lea something, [something+1] et jrcxz sont utilisés, qui sont apparemment plus efficaces que jnz / add something, size paire de add something, size nous aurions pu utiliser précédemment. Je ne suis pas sûr si cela a été découvert en testant simplement différentes instructions. Tu devrais demander.

Cependant, dans un message ultérieur, il est mesuré sur une puce AMD et ne fonctionne pas aussi bien.

Je suis également donné pour comprendre que différentes opérations fonctionnent différemment sur différents processeurs. Je sais, par exemple, que le projet GMP détecte les processeurs utilisant cpuid et transmet différentes routines d’assemblage basées sur différentes architectures, par exemple nehalem , nehalem .

La question que vous devez vous poser est la suivante: votre compilateur produit-il des résultats optimisés pour votre architecture de processeur? Le compilateur Intel, par exemple, est connu pour faire cela, il peut donc être utile de mesurer les performances et de voir quelle sortie il produit.

LEA n’est pas plus rapide que l’instruction ADD, la vitesse d’exécution est la même.

Mais LEA offre parfois plus que ADD . Si nous avons besoin d’une addition / multiplication simple et rapide en combinaison avec un deuxième registre, LEA peut accélérer l’exécution du programme. De l’autre côté, le LEA n’affecte pas les indicateurs de CPU, il n’y a donc pas de possibilité de détection de débordement.

Vous pouvez effectuer une instruction lea dans le même cycle d’horloge comme une opération d’ajout, mais si vous utilisez lea et que vous additionnez les deux, vous pouvez append trois opérandes en un seul cycle! Si vous utilisiez deux opérations d’ajout qui ne pouvaient être effectuées que sur deux cycles d’horloge:

 mov eax, [esp+4] ; get a from stack mov edx, [esp+8] ; get b from stack mov ecx, [esp+12] ; get c from stack lea eax, [eax+edx] ; add a and b in the adress decoding/fetch stage of the pipeline add eax, ecx ; Add c + eax in the execution stage of the pipeline ret 12