Comment activer les exceptions d’alignement pour mon processus sur x64?

Je suis curieux de voir si mon application 64 bits souffre de défauts d’alignement.

De l’ alignement de données Windows sur IPF, x86 et x64 :

Sous Windows, un programme d’application qui génère une erreur d’alignement déclenchera une exception, EXCEPTION_DATATYPE_MISALIGNMENT .

  • Sur l’architecture x64 , les exceptions d’alignement sont désactivées par défaut et les corrections sont effectuées par le matériel. L’application peut activer les exceptions d’alignement en définissant deux bits de registre , auquel cas les exceptions seront SEM_NOALIGNMENTFAULTEXCEPT moins que le système d’exploitation ne masque les exceptions avec SEM_NOALIGNMENTFAULTEXCEPT . (Pour plus de détails, voir le Manuel du programmeur d’architecture AMD, Volume 2: Programmation du système. )

[Ed. emphase sur moi]

  • Sur l’architecture x86 , le système d’exploitation ne rend pas l’erreur d’alignement visible pour l’application. Sur ces deux plates-formes, vous serez également confronté à une dégradation des performances du défaut d’alignement, mais il sera nettement moins grave que sur l’Itanium, car le matériel effectuera les multiples access mémoire pour récupérer les données non alignées.

  • Sur Itanium , par défaut, le système d’exploitation (OS) rendra cette exception visible pour l’application et un gestionnaire de terminaison peut être utile dans ces cas. Si vous ne configurez pas de gestionnaire, votre programme sera bloqué ou bloqué. Dans le Listing 3, nous fournissons un exemple qui montre comment intercepter l’exception EXCEPTION_DATATYPE_MISALIGNMENT.

En ignorant la direction de consulter le manuel du programmeur d’architecture AMD , je consulterai plutôt le manuel du développeur de logiciels d’architecture Intel 64 et IA-32.

5.10.5 Vérification de l’alignement

Lorsque la CPL est 3, l’alignement des références mémoire peut être vérifié en définissant le drapeau AM dans le registre CR0 et l’indicateur AC dans le registre EFLAGS. Les références mémoire non alignées génèrent des exceptions d’alignement (#AC). Le processeur ne génère pas d’exceptions d’alignement lorsqu’il opère au niveau de privilège 0, 1 ou 2. Voir Tableau 6-7 pour une description des exigences d’alignement lorsque la vérification de l’alignement est activée.

Excellent. Je ne suis pas sûr de ce que cela signifie, mais excellent.

Ensuite, il y a aussi:

2.5 REGISTRES DE CONTRÔLE

Les registres de contrôle (CR0, CR1, CR2, CR3 et CR4; voir Figure 2-6) déterminent le mode de fonctionnement du processeur et les caractéristiques de la tâche en cours d’exécution. Ces registres ont 32 bits dans tous les modes 32 bits et le mode de compatibilité.

En mode 64 bits, les registres de contrôle sont étendus à 64 bits. Les instructions CRV MOV sont utilisées pour manipuler les bits de registre. Les préfixes de taille d’opérande pour ces instructions sont ignorés.

Les registres de contrôle sont résumés ci-dessous et chaque champ de contrôle défini de manière architecturale dans ces registres de contrôle est décrit individuellement. Dans la Figure 2-6, la largeur du registre en mode 64 bits est indiquée entre parenthèses (sauf pour CR0). – CR0 – Contient des indicateurs de contrôle du système qui contrôlent le mode de fonctionnement et les états du processeur

entrer la description de l'image ici

UN M
Masque d’alignement (bit 18 de CR0) – Active la vérification d’alignement automatique lorsqu’il est défini; désactive la vérification d’alignement lorsqu’elle est désactivée. La vérification d’alignement est effectuée uniquement lorsque l’indicateur AM est défini, que l’indicateur AC du registre EFLAGS est défini, que CPL est égal à 3 et que le processeur fonctionne en mode protégé ou virtuel.

j’ai essayé

Le langage que j’utilise actuellement est Delphi, mais prétendez qu’il s’agit d’un pseudo-code agnostique de langage:

 void UnmaskAlignmentExceptions() { asm mov rax, cr0; //copy CR0 flags into RAX or rax, 0x20000; //set bit 18 (AM) mov cr0, rax; //copy flags back } 

La première instruction

 mov rax, cr0; 

échoue avec une exception d’instruction privilégiée.

Comment activer les exceptions d’alignement pour mon processus sur x64?

PUSHF

J’ai découvert que le x86 a l’instruction:

  • PUSHF , POPF : Poussez / PUSHF les premiers 16 bits d’EFLAGS sur / hors de la stack
  • PUSHFD , POPFD : Push / Pop tous les 32 bits de EFLAGS sur / hors de la stack

entrer la description de l'image ici

Cela m’a ensuite conduit à la version x64:

  • PUSHFQ , POPFQ : Poussez / POPFQ le quad RFLAGS sur la stack

(Dans le monde 64 bits, les EFLAGS sont renommés RFLAGS ).

Donc j’ai écrit:

 void EnableAlignmentExceptions; { asm PUSHFQ; //Push RFLAGS quadword onto the stack POP RAX; //Pop them flags into RAX OR RAX, $20000; //set bit 18 (AC=Alignment Check) of the flags PUSH RAX; //Push the modified flags back onto the stack POPFQ; //Pop the stack back into RFLAGS; } 

Et il n’a pas planté ou déclenché une exception de protection. Je ne sais pas si ça fait ce que je veux.

Lecture bonus

  • Comment attraper les fautes d’alignement de données sur x86 (aka SIGBUS sur Sparc) (question non liée; x86 pas x64, Ubunutu pas Windows, gcc vs not)

    Les applications exécutées sur x64 ont access à un registre d’indicateurs (parfois appelé EFLAGS ). Le bit 18 de ce registre permet aux applications d’obtenir des exceptions lorsque des erreurs d’alignement se produisent. Donc, en théorie, tout ce qu’un programme doit faire pour permettre des exceptions pour les erreurs d’alignement est de modifier le registre de drapeaux.

    toutefois

    Pour que cela fonctionne, le kernel du système d’exploitation doit définir le bit 18 de cr0 pour l’autoriser. Et le système d’exploitation Windows ne le fait pas. Pourquoi pas? Qui sait?

    Les applications ne peuvent pas définir de valeurs dans le registre de contrôle. Seul le kernel peut le faire. Les pilotes de périphérique s’exécutent à l’intérieur du kernel, ils peuvent donc également définir cela.

    Il est possible de créer un pilote de périphérique (voir http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx#199239 et les commentaires). qui suivent). Notez que ce post a plus de dix ans, donc certains liens sont morts.

    Vous pourriez également trouver ce commentaire (et certaines des autres réponses à cette question) utile:

    Larry Osterman – 28-07-2004 2h22

    Nous avons en fait construit une version de NT avec des exceptions d’alignement activées pour x86 (vous pouvez le faire comme mentionné dans Skywing).

    Nous l’avons rapidement désactivé, à cause du nombre d’applications qui ont cassé 🙂

    Cela fonctionne dans les processeurs Intel 64 bits. Peut échouer dans certains cas de DMLA

     pushfq bts qword ptr [rsp], 12h ; reset AC bit of rflags popfq 

    Cela ne fonctionnera pas tout de suite dans les processeurs 32 bits, il faudra d’abord un pilote du kernel pour modifier le bit AM de CR0, puis

     pushfd bts dword ptr [esp], 12h popfd 

    En guise d’alternative à la recherche de ralentissements dus à des access non alignés, vous pouvez utiliser des événements de compteur de performances matérielles sur les processeurs Intel pour mem_inst_retired.split_loads et mem_inst_retired.split_stores afin de trouver des charges réparties sur une limite de ligne de cache.

    perf record -c 10 -e mem_inst_retired.split_stores,mem_inst_retired.split_loads ./a.out devrait être utile sous Linux. -c 10 enregistre un échantillon tous les 10 événements matériels. Si votre programme effectue de nombreux access non alignés et que vous souhaitez uniquement rechercher les véritables zones sensibles, laissez-le à la valeur par défaut. Mais -c 10 peut obtenir des données utiles même sur un petit fichier binary qui appelle une fois printf. D’autres options telles que -g pour enregistrer les fonctions parentes sur chaque échantillon fonctionnent comme d’habitude et pourraient être utiles.

    Sous Windows, utilisez l’outil que vous préférez pour examiner les compteurs de perf. VTune est populaire.


    Les processeurs Intel modernes (famille P6 et plus récents) ne sont pas pénalisés en cas de défaut d’alignement au sein d’une ligne de cache . https://agner.org/optimize/ . En fait, ces charges / magasins sont même garantis atomiques (jusqu’à 8 octets) sur les processeurs Intel. Donc, AC est plus ssortingct que nécessaire, mais cela aidera à trouver des access potentiellement risqués qui pourraient être des fractionnements de pages ou des séparations de lignes de cache avec des données alignées différemment.

    Les CPU AMD peuvent avoir des pénalités pour traverser une limite de 16 octets dans une ligne de cache de 64 octets . Je ne suis pas familier avec les compteurs de matériel disponibles. Attention, le profilage sur Intel HW ne détectera pas nécessairement les ralentissements survenant sur les processeurs AMD si l’access en question ne dépasse jamais les limites d’une ligne de cache.

    Reportez-vous à la section Comment puis-je évaluer avec précision la vitesse d’access non alignée sur x86_64 pour obtenir des informations détaillées sur les pénalités, y compris mes tests sur la latence fractionnée 4k et le débit sur Skylake.

    Voir aussi http://blog.stuffedcow.net/2014/01/x86-memory-disambiguation/ pour d’éventuelles pénalités à l’efficacité du transfert de stockage pour les charges / magasins mal alignés sur Intel / AMD.


    L’exécution de fichiers binarys normaux avec le réglage AC n’est pas toujours pratique . Le code généré par le compilateur peut choisir d’utiliser un chargement ou un magasin non aligné de 8 octets pour copier plusieurs membres de structure ou pour stocker certaines données littérales.

    gcc -O3 -mtune=generic (c’est-à-dire que l’optimisation est activée par défaut) suppose que les fractionnements de ligne de cache sont suffisamment bon marché pour justifier le risque d’utiliser des access non alignés au lieu de plusieurs access étroits. Les fractionnements de page sont devenus beaucoup moins chers chez Skylake, passant de 100 à 150 cycles chez Haswell à environ 10 cycles chez Skylake (soit à peu près la même pénalité que les clivages de CL).

    De nombreuses fonctions de bibliothèque optimisées (comme memcpy ) utilisent des access entiers non alignés. Par exemple, le memcpy de glibc, pour une copie de 6 octets, ferait 2 charges de 4 octets se chevauchant depuis le début / la fin du tampon, puis 2 magasins se chevauchant. (Il n’y a pas de cas particulier pour exactement 6 octets pour faire un mot dword +, en augmentant simplement les puissances de 2). Ce commentaire dans la source explique ses stratégies.

    Donc, même si votre système d’exploitation vous permettait d’activer le courant alternatif, vous pourriez avoir besoin d’une version spéciale de bibliothèques pour ne pas déclencher la memcpy , comme les petits memcpy .


    L’alignement en boucle sur un tableau est important pour AVX512, où un vecteur a la même largeur qu’une ligne de cache. Si vos pointeurs sont mal alignés, chaque access est un partage de ligne de cache, pas seulement avec AVX2. Aligned est toujours mieux, mais pour de nombreux algorithmes avec une quantité décente de calculs mélangés avec un access mémoire, cela ne fait qu’une différence significative avec AVX512.

    Les access désalignés dispersés traversent une limite de ligne de cache et ont essentiellement deux fois l’empreinte de cache de toucher les deux lignes, si les lignes ne sont pas touchées autrement.