Modes de micro fusion et d’adressage

J’ai trouvé quelque chose d’inattendu (à mon avis) en utilisant l’IACA ( Intel® Architecture Code Analyzer ).

L’instruction suivante utilisant l’adressage [base+index]

 addps xmm1, xmmword ptr [rsi+rax*1] 

ne micro-fusible pas selon IACA. Cependant, si j’utilise [base+offset] comme ça

 addps xmm1, xmmword ptr [rsi] 

L’IACA signale qu’elle fusionne.

La section 2-11 du manuel de référence d’optimisation Intel donne l’exemple suivant “de micro-opérations micro-fusionnées pouvant être traitées par tous les décodeurs”

 FADD DOUBLE PTR [RDI + RSI*8] 

Le manuel d’assemblage d’optimisation d’Agner Fog donne également des exemples de fusion micro-op utilisant l’adressage [base+index] . Voir, par exemple, la section 12.2 “Même exemple sur Core2”. Alors, quelle est la bonne réponse?

Dans les décodeurs et les cache-uop, le mode d’adressage n’affecte pas la micro-fusion (sauf qu’une instruction avec un opérande immédiat ne peut pas fusionner un mode d’adressage relatif au RIP).

Mais certaines combinaisons de mode uop et d’adressage ne peuvent pas restr micro-fusionnées dans le ROB (dans le kernel hors service), de sorte que les processeurs de la famille Intel SnB “se décollent” lorsque cela est nécessaire, renommer l’étape. Pour la taille de la fenêtre d’émission et de la fenêtre hors service (taille ROB), le nombre de domaines fusionnés est pris en compte après la suppression de la lamination.

Le manuel d’optimisation d’Intel décrit la désincrustation de Sandybridge dans la section 2.3.2.4: File d’attente micro-op et le détecteur de stream de boucle (LSD) , mais ne décrit pas les modifications pour les microarchitectures ultérieures.


Les règles , du mieux que je peux tirer d’expériences sur SnB, HSW et SKL:

  • SnB (et je suppose également IvB): les modes d’adressage indexés sont toujours non laminés, d’autres restnt micro-fusionnés. IACA est (principalement?) Correct.
  • HSW, SKL: Ceux-ci ne conservent qu’une instruction d’indexation ALU micro-fusionnée si elle a 2 opérandes et traite le registre dst en lecture-modification-écriture. Ici, les “opérandes” incluent les drapeaux, ce qui signifie que adc et cmov ne font pas de micro-fusible. La plupart des instructions encodées en VEX ne fusionnent pas non plus car elles ont généralement trois opérandes (donc paddb xmm0, [rdi+rbx] fusionnent mais vpaddb xmm0, xmm0, [rdi+rbx] ne le font pas). Enfin, les instructions à 2 opérandes occasionnelles où le premier opérande est en écriture seule, telles que pabsb xmm0, [rax + rbx] ne fusionnent pas non plus. IACA a tort d’appliquer les règles SnB.

Connexe: les modes d’adressage simples (non indexés) sont les seuls que l’unité d’adresse de stockage dédiée sur port7 (Haswell et versions ultérieures) peut gérer. Il est donc potentiellement utile d’éviter les modes d’adressage indexés pour les magasins. (Un bon truc pour cela est de traiter votre dst avec un seul registre, mais avec src avec dst+(initial_src-initial_dst) . Ensuite, il suffit d’incrémenter le registre dst dans une boucle.)

Notez que certaines instructions ne micro-fusionnent jamais du tout (même dans les décodeurs / uop-cache). Par exemple, shufps xmm, [mem], imm8 ou vinsertf128 ymm, ymm, [mem], imm8 , sont toujours 2 uops sur SnB via Skylake, même si leurs versions source de registre ne sont que 1 uop. Ceci est typique pour les instructions avec un opérande de contrôle imm8 plus les opérandes habituels de registre / mémoire dest / src1, src2, mais il y a quelques autres cas. Par exemple, PSRLW/D/Q xmm,[mem] (nombre de décalages vectoriels à partir d’un opérande de mémoire) ne fait pas de micro-fusion et PMULLD non plus.

Voir également ce post sur le blog d’Agner Fog pour des discussions sur les limites de débit sur HSW / SKL lorsque vous lisez beaucoup de registres: beaucoup de micro-fusion avec modes d’adressage indexés peuvent entraîner des ralentissements par rapport aux mêmes instructions avec moins d’opérandes de registre: enregistrer les modes d’adressage et les imprévus. Nous ne connaissons pas encore la cause, mais je soupçonne qu’une sorte de limite de lecture de registre, peut-être liée à la lecture de nombreux registres froids du PRF.


Cas de test, numéros de mesures réelles : Ils sont tous micro-fusibles dans les décodeurs, AFAIK, même s’ils ne sont pas laminés ultérieurement.

 # store mov [rax], edi SnB/HSW/SKL: 1 fused-domain, 2 unfused. The store-address uop can run on port7. mov [rax+rsi], edi SnB: unlaminated. HSW/SKL: stays micro-fused. (The store-address can't use port7, though). mov [buf +rax*4], edi SnB: unlaminated. HSW/SKL: stays micro-fused. # normal ALU stuff add edx, [rsp+rsi] SnB: unlaminated. HSW/SKL: stays micro-fused. # I assume the majority of traditional/normal ALU insns are like add 

Instructions à trois entrées que HSW / SKL peut devoir décoller

 vfmadd213ps xmm0,xmm0,[rel buf] HSW/SKL: stays micro-fused: 1 fused, 2 unfused. vfmadd213ps xmm0,xmm0,[rdi] HSW/SKL: stays micro-fused vfmadd213ps xmm0,xmm0,[0+rdi*4] HSW/SKL: un-laminated: 2 uops in fused & unfused-domains. (So indexed addressing mode is still the condition for HSW/SKL, same as documented by Intel for SnB) # no idea why this one-source BMI2 instruction is unlaminated # It's different from ADD in that its destination is write-only (and it uses a VEX encoding) blsi edi, [rdi] HSW/SKL: 1 fused-domain, 2 unfused. blsi edi, [rdi+rsi] HSW/SKL: 2 fused & unfused-domain. adc eax, [rdi] same as cmov r, [rdi] cmove ebx, [rdi] Stays micro-fused. (SnB?)/HSW: 2 fused-domain, 3 unfused domain. SKL: 1 fused-domain, 2 unfused. # I haven't confirmed that this micro-fuses in the decoders, but I'm assuming it does since a one-register addressing mode does. adc eax, [rdi+rsi] same as cmov r, [rdi+rsi] cmove ebx, [rdi+rax] SnB: untested, probably 3 fused&unfused-domain. HSW: un-laminated to 3 fused&unfused-domain. SKL: un-laminated to 2 fused&unfused-domain. 

Je suppose que Broadwell se comporte comme Skylake pour adc / cmov.

Il est étrange que HSW supprime les ADC et CMOV source de mémoire. Peut-être qu’Intel n’a pas réussi à changer cela depuis SnB avant qu’ils n’atteignent la date limite pour l’envoi de Haswell.

Le tableau inséré par Agner indique que cmovcc r,m et adc r,m ne micro-fusible pas du tout sur HSW / SKL, mais cela ne correspond pas à mes expériences. Le cycle compte. Je mesure la correspondance avec le nombre de problèmes de domaine avec fusible, pour un goulot d’étranglement de 4 uops / horloge. J’espère qu’il va vérifier cela et corriger les tableaux.

Mémoire-dest entier ALU :

 add [rdi], eax SnB: untested (Agner says 2 fused-domain, 4 unfused-domain (load + ALU + store-address + store-data) HSW/SKL: 2 fused-domain, 4 unfused. add [rdi+rsi], eax SnB: untested, probably 4 fused & unfused-domain HSW/SKL: 3 fused-domain, 4 unfused. (I don't know which uop stays fused). HSW: About 0.95 cycles extra store-forwarding latency vs. [rdi] for the same address used repeatedly. (6.98c per iter, up from 6.04c for [rdi]) SKL: 0.02c extra latency (5.45c per iter, up from 5.43c for [rdi]), again in a tiny loop with dec ecx/jnz adc [rdi], eax SnB: untested HSW: 4 fused-domain, 6 unfused-domain. (same-address throughput 7.23c with dec, 7.19c with sub ecx,1) SKL: 4 fused-domain, 6 unfused-domain. (same-address throughput ~5.25c with dec, 5.28c with sub) adc [rdi+rsi], eax SnB: untested HSW: 5 fused-domain, 6 unfused-domain. (same-address throughput = 7.03c) SKL: 5 fused-domain, 6 unfused-domain. (same-address throughput = ~5.4c with sub ecx,1 for the loop branch, or 5.23c with dec ecx for the loop branch.) 

Oui, c’est vrai, adc [rdi],eax / dec ecx / jnz s’exécute plus vite que la même boucle avec add au lieu de adc sur SKL. Je n’ai pas essayé d’utiliser des adresses différentes, car SKL n’aime pas les réécritures répétées de la même adresse (latence de réacheminement plus élevée que prévu). Voir aussi cet article sur le stockage répété / le rechargement à la même adresse .

La mémoire adc destination est tellement importante car la famille Intel P6 (et apparemment la famille SnB) ne peut pas conserver les mêmes entrées TLB pour tous les uops d’une instruction multi-uop, elle a donc besoin d’un uop supplémentaire pour contourner le problème -case où le chargement et l’ajout sont terminés, puis les défauts du magasin, mais l’insn ne peut pas simplement être redémarré car CF a déjà été mis à jour . Série intéressante de commentaires d’Andy Glew (@krazyglew).

On peut supposer que la fusion dans les décodeurs et le décollement plus tard nous évitent d’ avoir besoin de microcode ROM pour produire plus de 4 uops à domaine fusionné à partir d’une seule instruction pour adc [base+idx], reg .


Pourquoi la famille SnB dés-plastifie :

Sandybridge a simplifié le format UOP interne pour économiser de l’énergie et des transistors (tout en apportant le changement majeur à l’utilisation d’un fichier de registre physique, au lieu de conserver les données d’entrée / sortie dans le ROB). Les processeurs de la famille SnB n’autorisent qu’un nombre limité de registres d’entrée pour un uop de domaine fusionné dans le kernel hors service. Pour SnB / IvB, cette limite est de 2 entrées (drapeaux compris). Pour HSW et les versions ultérieures, la limite est de 3 entrées pour un uop. Je ne sais pas si la destination de la mémoire et l’ adc en tirent pleinement parti, ou si Intel a dû demander à Haswell quelques instructions

Nehalem et les versions antérieures ont une limite de 2 entrées pour un uop de domaine non fusionné, mais le ROB peut apparemment suivre les uops micro-fusionnés avec 3 registres d’entrée (l’opérande, la base et l’index du registre non-mémoire).


Ainsi, les magasins indexés et les instructions de chargement ALU + peuvent toujours décoder efficacement (sans avoir à être le premier uop dans un groupe), et ne prennent pas d’espace supplémentaire dans le cache uop, mais les avantages de la micro-fusion boucles serrées. La “décollementation” se produit avant le kernel hors ordre de largeur / retrait de 4-fusionné-domaine-uops-par cycle . Les compteurs de performance du domaine fusionné (uops_issued / uops_retired.retire_slots) comptent les uops du domaine fusionné après le décollage.

La description d’Intel du renamer ( Section 2.3.3.1: Renamer ) implique que c’est le problème / l’étape de renommage qui fait réellement le décollement, donc les uops destinés à la décollage peuvent toujours être fondus dans le 28/56/64. -domain uop queue / loop-buffer (alias IDQ).

TODO: testez ceci. Faites une boucle qui devrait juste entrer dans le tampon de boucle. Changez quelque chose pour que l’un des uops ne soit pas plastifié avant d’être lancé, et voyez s’il fonctionne toujours à partir du buffer de boucle (LSD), ou si tous les uops sont à nouveau extraits du cache uop (DSB). Il y a des compteurs de perf pour suivre d’où viennent les uops, donc cela devrait être facile.

TODO plus difficile: si la lamination se produit entre la lecture du cache uop et l’ajout à l’IDQ, testez si elle peut réduire la bande passante du cache uop. Ou, si le décollement se produit au stade de la publication, peut-il nuire au débit? (c.-à-d. comment gère-t-il les uops restants après l’émission des 4 premiers)


(Voir la version précédente de cette réponse pour quelques suppositions basées sur le réglage de code LUT, certaines notes sur vpgatherdd étant d’environ 1,7 fois plus de cycles qu’une boucle de pinsrw .)

Tests expérimentaux sur SnB

Les nombres HSW / SKL ont été mesurés sur un i5-4210U et un i7-6700k. Les deux avaient le HT activé (mais le système était inactif alors le thread avait tout le coeur). J’ai exécuté les mêmes binarys statiques sur les deux systèmes, Linux 4.10 sur SKL et Linux 4.8 sur HSW, en utilisant ocperf.py . (Le portable HSW monté sur NFS a mon bureau / bureau SKL.)

Les nombres de SnB ont été mesurés comme décrit ci-dessous, sur un i5-2500k qui ne fonctionne plus.

Confirmé par des tests avec des compteurs de performance pour les uops et les cycles.

J’ai trouvé un tableau des événements PMU pour Intel Sandybridge , à utiliser avec la commande perf de Linux. (La perf standard n’a malheureusement pas de noms symboliques pour la plupart des événements PMU spécifiques au matériel, tels que les uops.) Je les ai utilisés pour une réponse récente .

ocperf.py fournit des noms symboliques pour ces événements PMU spécifiques à un uarch , vous n’avez donc pas besoin de rechercher des tables. En outre, le même nom symbolique fonctionne sur plusieurs uarches. Je n’étais pas au courant quand j’ai écrit cette réponse pour la première fois.

Pour tester la micro-fusion, j’ai conçu un programme de test qui gêne le nombre de domaines fusionnés à 4 niveaux par cycle des processeurs Intel. Pour éviter tout conflit de ports d’exécution, beaucoup de ces uops sont des nop s, qui sont toujours dans le cache uop et passent par le pipeline comme n’importe quel autre uop, sauf qu’ils ne sont pas envoyés sur un port d’exécution. (Un mouvement xor x, same , ou un mouvement éliminé, serait le même.)

Programme de test: yasm -f elf64 uop-test.s && ld uop-test.o -o uop-test

 GLOBAL _start _start: xor eax, eax xor ebx, ebx xor edx, edx xor edi, edi lea rsi, [rel mydata] ; load pointer mov ecx, 10000000 cmp dword [rsp], 2 ; argc >= 2 jge .loop_2reg ALIGN 32 .loop_1reg: or eax, [rsi + 0] or ebx, [rsi + 4] dec ecx nop nop nop nop jg .loop_1reg ; xchg r8, r9 ; no effect on flags; decided to use NOPs instead jmp .out ALIGN 32 .loop_2reg: or eax, [rsi + 0 + rdi] or ebx, [rsi + 4 + rdi] dec ecx nop nop nop nop jg .loop_2reg .out: xor edi, edi mov eax, 231 ; exit(0) syscall SECTION .rodata mydata: db 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 

J’ai également constaté que la bande passante en sortie du buffer de boucle n’est pas une constante de 4 par cycle si la boucle n’est pas un multiple de 4 uops. (c’est à dire abc , abc , …; pas abca , bcab , …). Le document microarch de Agner Fog n’était malheureusement pas clair sur cette limitation du buffer de boucle. Voir Est-ce que la performance est réduite lors de l’exécution de boucles dont le compte uop n’est pas un multiple de la largeur du processeur? pour plus d’enquête sur HSW / SKL. SnB peut être pire que HSW dans ce cas, mais je ne suis pas sûr et je n’ai toujours pas de matériel SnB fonctionnel.

Je voulais garder la macro-fusion (compare-and-branch) hors de l’image, donc j’ai utilisé nop s entre le dec et la twig. J’ai utilisé 4 nop s, donc avec la micro-fusion, la boucle aurait 8 uops et remplirait le pipeline avec 2 cycles par 1 itération.

Dans l’autre version de la boucle, utilisant des modes d’adressage à 2 opérandes qui ne sont pas micro-fusibles, la boucle sera composée de 10 uops de domaine fusionnés et exécutera 3 cycles.

Résultats de mon processeur Intel Sandybridge à 3,3 GHz (i5 2500k). Je n’ai rien fait pour amener le régulateur cpufreq à augmenter la vitesse d’horloge avant le test, car les cycles sont des cycles lorsque vous n’interagissez pas avec la mémoire. J’ai ajouté des annotations pour les compteurs de performance que je devais entrer en hexadécimal.

tester le mode d’adressage 1-reg: no cmdline arg

 $ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test Performance counter stats for './uop-test': 11.489620 task-clock (msec) # 0.961 CPUs utilized 20,288,530 cycles # 1.766 GHz 80,082,993 instructions # 3.95 insns per cycle # 0.00 stalled cycles per insn 60,190,182 r1b1 ; UOPS_DISPATCHED: (unfused-domain. 1->umask 02 -> uops sent to execution ports from this thread) 80,203,853 r10e ; UOPS_ISSUED: fused-domain 80,118,315 r2c2 ; UOPS_RETIRED: retirement slots used (fused-domain) 100,136,097 r1c2 ; UOPS_RETIRED: ALL (unfused-domain) 220,440 stalled-cycles-frontend # 1.09% frontend cycles idle 193,887 stalled-cycles-backend # 0.96% backend cycles idle 0.011949917 seconds time elapsed 

tester le mode d’adressage 2-reg: avec une ligne de commande cmdline

 $ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test x Performance counter stats for './uop-test x': 18.756134 task-clock (msec) # 0.981 CPUs utilized 30,377,306 cycles # 1.620 GHz 80,105,553 instructions # 2.64 insns per cycle # 0.01 stalled cycles per insn 60,218,693 r1b1 ; UOPS_DISPATCHED: (unfused-domain. 1->umask 02 -> uops sent to execution ports from this thread) 100,224,654 r10e ; UOPS_ISSUED: fused-domain 100,148,591 r2c2 ; UOPS_RETIRED: retirement slots used (fused-domain) 100,172,151 r1c2 ; UOPS_RETIRED: ALL (unfused-domain) 307,712 stalled-cycles-frontend # 1.01% frontend cycles idle 1,100,168 stalled-cycles-backend # 3.62% backend cycles idle 0.019114911 seconds time elapsed 

Ainsi, les deux versions exécutaient des instructions 80M et envoyaient 60Mo aux ports d’exécution. ( or avec une source de mémoire dissortingbue à un ALU pour le or , et un port de chargement pour le chargement, que celui-ci soit fusionné ou non dans le rest du pipeline. nop ne parvient pas du tout à un port d’exécution .) De même, les deux versions retirent 100 uops de domaine non fusionnés, car les nops de 40M comptent ici.

La différence réside dans les compteurs du domaine fusionné.

  1. La version d’adresse à 1 registre émet uniquement et supprime 80 uops de domaine fusionné. C’est le même que le nombre d’instructions. Chaque insn se transforme en un uop de domaine fusionné.
  2. La version d’adresse à 2 registres génère 100 uops de domaine fusionné. C’est la même chose que le nombre de uops de domaine non fusionné, indiquant qu’aucune micro-fusion n’a eu lieu.

Je soupçonne que vous ne verriez une différence qu’entre UOPS_ISSUED et UOPS_RETIRED (créneaux de retraite utilisés) si les erreurs de publication des succursales entraînaient une annulation après publication, mais avant la retraite.

Et finalement, l’impact sur la performance est réel. La version non fusionnée a nécessité 1,5 fois plus de cycles d’horloge. Cela exagère la différence de performance par rapport à la plupart des cas réels. La boucle doit être exécutée dans un nombre entier de cycles, et les 2 uops supplémentaires le font passer de 2 à 3. Souvent, un uops de domaine fusionné supplémentaire fera moins de différence. Et potentiellement aucune différence, si le code est embouteillé par autre chose que 4-fusionné-domaine-uops-par-cycle.

Néanmoins, le code qui fait beaucoup de références mémoire dans une boucle peut être plus rapide s’il est implémenté avec une quantité modérée de déroulement et d’incrémentation de plusieurs pointeurs qui sont utilisés avec un adressage simple [base + immediate offset] au lieu de [base + index] modes d’adressage.

d’autres choses

RIP-relative avec un immédiat ne peut pas micro-fusible . Les tests d’Agner Fog montrent que c’est le cas même dans les décodeurs / cache UOP, ils ne fusionnent donc jamais (plutôt que d’être non laminés).

IACA se trompe et affirme que ces deux micro-fusibles:

 cmp dword [abs mydata], 0x1b ; fused counters != unfused counters (micro-fusion happened, and wasn't un-laminated). Uses 2 ensortinges in the uop-cache, according to Agner Fog's testing cmp dword [rel mydata], 0x1b ; fused counters ~= unfused counters (micro-fusion didn't happen) 

RIP-rel fait un micro-fusible (et rest soudé) quand il n’y a pas de contact immédiat, par exemple:

 or eax, dword [rel mydata] ; fused counters != unfused counters, ie micro-fusion happens 

La micro-fusion n’augmente pas la latence d’une instruction . Le chargement peut émettre avant que l’autre entrée ne soit prête.

 ALIGN 32 .dep_fuse: or eax, [rsi + 0] or eax, [rsi + 0] or eax, [rsi + 0] or eax, [rsi + 0] or eax, [rsi + 0] dec ecx jg .dep_fuse 

Cette boucle fonctionne à 5 cycles par itération, à cause de la chaîne eax dep. Pas plus rapide qu’une séquence de or eax, [rsi + 0 + rdi] , ou mov ebx, [rsi + 0 + rdi] / or eax, ebx . (Les versions non fusionnée et mov exécutent toutes deux le même nombre de uops.) La vérification de planification / dep se produit dans le domaine non fusionné. Les uops récemment publiés vont dans le planificateur (aka Reservation Station (RS)) ainsi que le ROB. Ils quittent l’ordonnanceur après l’envoi (alias être envoyé à une unité d’exécution), mais restnt dans l’ORB jusqu’à leur retraite. Donc, la fenêtre hors service pour cacher la latence de la charge est au moins la taille du planificateur ( 54 uops de domaine non fusionnés dans Sandybridge, 60 dans Haswell , 97 dans Skylake).

La micro-fusion n’a pas de raccourci pour la base et le décalage étant le même registre. Une boucle avec or eax, [mydata + rdi+4*rdi] (où rdi est mis à zéro) exécute autant d’ops et de cycles que la boucle avec or eax, [rsi+rdi] . Ce mode d’adressage pourrait être utilisé pour itérer sur un tableau de structures de taille irrégulière à partir d’une adresse fixe. Ce n’est probablement jamais utilisé dans la plupart des programmes, donc il n’est pas surprenant qu’Intel n’ait pas dépensé de transistors pour permettre à ce cas particulier de modes à 2 registres de fusionner. (Et Intel le documente de toute façon comme “modes d’adressage indexés”, où un registre et un facteur d’échelle sont nécessaires.)


La macro-fusion d’un cmp / jcc ou dec / jcc crée un uop qui rest un uop unique même dans le domaine non fusionné. dec / nop / jge peut toujours s’exécuter en un seul cycle, mais trois fois au lieu d’un.

Note: Depuis que j’ai écrit cette réponse, Peter a également testé Haswell et Skylake et a intégré les résultats dans la réponse acceptée ci-dessus (en particulier, la plupart des améliorations que j’atsortingbue à Skylake ci-dessous semblent être apparues dans Haswell). Vous devriez voir cette réponse à la dégradation du comportement à travers les processeurs et cette réponse (même si elle n’est pas erronée) présente surtout un intérêt historique.

Mes tests indiquent que sur Skylake au moins 1 , le processeur fusionne complètement les modes d’adressage complexes, contrairement à Sandybridge.

C’est -dire que les versions 1-arg et 2-arg du code publié ci-dessus par Peter s’exécutent dans le même nombre de cycles, avec le même nombre de Uops dissortingbués et retirés.

Mes résultats:

Statistiques du compteur de performance pour ./uop-test :

  23.718772 task-clock (msec) # 0.973 CPUs utilized 20,642,233 cycles # 0.870 GHz 80,111,957 instructions # 3.88 insns per cycle 60,253,831 uops_executed_thread # 2540.344 M/sec 80,295,685 uops_issued_any # 3385.322 M/sec 80,176,940 uops_retired_retire_slots # 3380.316 M/sec 0.024376698 seconds time elapsed 

Statistiques du compteur de performance pour ./uop-test x :

  13.532440 task-clock (msec) # 0.967 CPUs utilized 21,592,044 cycles # 1.596 GHz 80,073,676 instructions # 3.71 insns per cycle 60,144,749 uops_executed_thread # 4444.487 M/sec 80,162,360 uops_issued_any # 5923.718 M/sec 80,104,978 uops_retired_retire_slots # 5919.478 M/sec 0.013997088 seconds time elapsed 

Statistiques du compteur de performance pour ./uop-test xx :

  16.672198 task-clock (msec) # 0.981 CPUs utilized 27,056,453 cycles # 1.623 GHz 80,083,140 instructions # 2.96 insns per cycle 60,164,049 uops_executed_thread # 3608.645 M/sec 100,187,390 uops_issued_any # 6009.249 M/sec 100,118,409 uops_retired_retire_slots # 6005.112 M/sec 0.016997874 seconds time elapsed 

Je n’ai trouvé aucune instruction UOPS_RETIRED_ANY sur Skylake, seulement le type “retraité” qui est apparemment fusionné.

Le test final ( uop-test xx ) est une variante que Peter suggère, qui utilise un cmp relatif au RIP avec immédiat, connu pour ne pas microfuser:

 .loop_riprel cmp dword [rel mydata], 1 cmp dword [rel mydata], 2 dec ecx nop nop nop nop jg .loop_riprel 

Les résultats montrent que les 2 uops supplémentaires par cycle sont captés par les compteurs émis et retirés (par conséquent, le test peut différencier la fusion et non).

D’autres tests sur d’autres architectures sont les bienvenus! Vous pouvez trouver le code (copié de Peter ci-dessus) dans github .


[1] … et peut-être d’autres architectures entre Skylake et Sandybridge, puisque Peter n’a testé que SB et que j’ai seulement testé SKL.

Les processeurs Intel plus anciens sans cache UOP peuvent faire la fusion, ce qui constitue peut-être un inconvénient du cache UOP. Je n’ai pas le temps de tester cela maintenant, mais j’appendai un test pour uop fusion la prochaine fois que je mettrai à jour mes scripts de test . Avez-vous essayé avec les instructions FMA? Ce sont les seules instructions qui autorisent 3 dépendances d’entrée dans un UOP non fusionné.

J’ai maintenant passé en revue les résultats des tests pour Intel Sandy Bridge, Ivy Bridge, Haswell et Broadwell. Je n’ai pas encore eu access à un test sur Skylake. Les résultats sont les suivants:

  • Les instructions avec un adressage à deux registres et trois dépendances d’entrée fusionnent parfaitement. Ils ne prennent qu’une seule entrée dans le cache de micro-opération tant qu’ils ne contiennent pas plus de 32 bits de données (ou 2 * 16 bits).
  • Il est possible de créer des instructions avec quatre dépendances d’entrée, en utilisant des instructions de multiplication et d’ajout fusionnées sur Haswell et Broadwell. Ces instructions fusionnent toujours en une seule micro-op et ne prennent qu’une seule entrée dans le cache micro-op.
  • Les instructions avec plus de 32 bits de données, par exemple l’adresse de 32 bits et les données immédiates de 8 bits, peuvent toujours fusionner, mais utiliser deux entrées dans le cache de la micro-opération (à moins que les 32 bits puissent être compressés dans un entier signé de 16 bits)
  • Les instructions avec un adressage relatif et une constante immédiate ne fusionnent pas, même si le décalage et la constante immédiate sont très faibles.
  • Tous les résultats sont identiques sur les quatre machines testées.
  • Les tests ont été effectués avec mes propres programmes de test en utilisant les compteurs de surveillance des performances sur des boucles suffisamment petites pour tenir dans le cache micro-op.

Vos résultats peuvent être dus à d’autres facteurs. Je n’ai pas essayé d’utiliser l’IACA.