[gull] Above Meltdown & Spectre

Dominik Madon dominik at acm.org
Thu Feb 8 07:05:49 CET 2018


> Sachant que la majorité du code se résume à l'exécution de LOAD & STORE (mesure sur des millions de lignes de code ayant permit de définir l'architecture HP-PA, ~80% !!!), je demande à voir l'effet sur un serveur WEB ou de base de données... Le monde se focalise essentiellement sur des micro-benchmarks débiles qui n'ont quasi aucun impact dans le monde réel. Soyons sérieux, si les techniques de limitation du "branch misprediction" sont utiles dans certain cas, sont impact est souvent largement surévalué. Qui plus est, la plupart du temps ces "techniques" se résument à faire une moyenne sur les trois derniers branchements à cette adresse... et encore ne faut-il pas en avoir trop... car les registres de "misprediction" sont très limités... La taille, et surtout l'architecture, de la cache et du TLB ont beaucoup plus d'impact que cette fameuse technique de "misprediction".

Je ne sais pas ce que c’est que la technique de « misprediction". Je connais, pour ma part, ce qu’est l’exécution spéculative et celle-ci est _fondamentale_ dans les performances actuelles des processeurs à long pipeline et exécution en parallèle dans le désordre.

Le pipeline du Cortex-M15 de l’ARM, par exemple, compte 15 étages et permet d’exécuter 3 instructions en parallèle par cycle au maximum. Il peut en théorie traiter simultanément jusqu’à 59 instructions à différentes étapes de leur exécution. Voici un morceau de code que je viens d’extraire d’un de mes programmes en ligne de commande que j’utilise régulièrement (ce n’est _pas_ du benchmark):

3e958: sub     sp, #16
3e95a: str.w   lr, [sp, #12]
3e95e: ldr     r4, [r1, #20]
3e960: cmp     r0, r4
3e962: bne.n   3e96e
3e964: ldr.w   lr, [sp, #12]
3e968: movs    r0, #1
3e96a: add     sp, #16
3e96c: bx      lr
3e96e: ldr     r2, [r1, #12]
3e970: movs    r7, #1
3e972: ldr.w   r5, [r2, #-4]
3e976: asrs    r3, r0, #1
3e978: lsrs    r6, r5, #10
3e97a: rsb     ip, r7, r6, lsl #2
3e97e: adds.w  r4, r2, ip
3e982: ldrb    r4, [r4, #0]
3e984: movs    r5, #1
3e986: subs.w  r4, ip, r4
3e98a: cmp     r4, r3
3e98c: bls.n   3e9c0

Il y a trois branchements (adresses 3e962, 3e96c et 3e98c) sur les 21 lignes de code (14% de branchements). Si l’on ne fait aucune prédiction de branchement, on peut faire passer en moyenne 6 instructions dans le pipeline avant de bloquer pendant 14 cycles (5 étages fetch, 7 dispatch, 1 issue et 2 execution) avant de savoir si le saut est pris ou pas ou d’avoir la valeur du link register. C’est donc 14 cycles d’attente pour 2 à 6 cycles d’exécution (en fonction du parallélisme possible des unité d’exécution). Cela veut dire que le processeur ne fait rien entre 14/20 et 14/16 de son temps soit, respectivement, entre 70% et 87% de son temps !

En n’utilisant aucune prédiction intelligente (sans BTB, BHB, etc.) et en se contentant de faire de l’exécution spéculative avec du pris systématique ou du pas pris systématique, on va considérablement augmenter la performance rien qu’en se basant sur les boucles qui sont présentes dans le code (for, do, while, etc): plusieurs tours avec une prédiction correcte et une sortie sur une prédiction fausse. C’était le principe utilisé dans le premier Pentium. Rien qu’en faisant juste 50% de son temps sur le code ci-dessus (on a bien entendu pas de solution pour le branchement indirect au link register de l’adresse 3e96c), on passe à 50% et, respectivement, 63% de slots de traitements du processeurs non utilisés ou à jeter en raison de la mauvaise prédiction. Imaginez l’ajout d'un BHT (branch history buffer) et d'un BTB (branch target buffer) donnant plus de 90% de prédictions correctes, ce que montrent la plupart des articles sur du code standard, et vous comprendrez pourquoi ne pas faire de prédiction c’est diviser par 3 ou 4 les performances d’un CPU. 

C’est simple, les pipelines sont tellement longs et larges que les sauts, même en petites quantités, viennent tout le temps bloquer le pipeline en forçant le passage de bulles (instructions qui ne font rien) à faire progresser dans le pipeline dans l’attente d’un résultat de comparaison ou de calcul. Si l’on a 5% de sauts dans du code, c’est 3 instructions de saut en moyenne dans le pipeline d'un Cortex-M15. Si l’on en a 14%, comme dans mon exemple ci-dessus, c’est 8 instructions de saut en moyenne. Sans spéculation, vous pouvez enlever la boîte 6 vitesses de votre Ferrari et la remplacer par une vitesse unique (la première) et ne jamais pousser l’accélérateur au max.


Dom




More information about the gull mailing list