1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • L’émulateur x86-32 générait du code natif par traduction binaire afin d’exécuter du code x86-32 sur d’autres processeurs, offrant un net gain de performances par rapport à une approche par interprétation
  • On peut comprendre cet émulateur comme une architecture qui traite le x86-32 comme du bytecode et fait fonctionner l’émulateur comme un compilateur JIT
  • Un programme devait allouer puis initialiser environ 64 Ko de mémoire sur la pile ; la méthode habituelle consistait à effectuer un sondage de pile, puis à réduire le pointeur de pile et à initialiser la mémoire avec une petite boucle
  • Le compilateur de ce code a généré non pas une boucle, mais 65 536 instructions individuelles d’écriture d’octet, chaque instruction faisant 4 octets, si bien qu’il fallait 256 Ko de code pour initialiser 64 Ko de données
  • L’équipe de l’émulateur a ajouté au traducteur un code spécial pour détecter cette fonction et la remplacer par une courte boucle équivalente

Contexte : émulateur x86-32 et traduction binaire

  • Windows a déjà intégré un émulateur de processeur x86-32 pour des systèmes fonctionnant sur des processeurs autres que x86-32
  • Le texte original ne précise pas à quel processeur ce cas s’appliquait
  • Cet émulateur utilisait la traduction binaire pour générer du code natif reproduisant un comportement équivalent à celui du code x86-32 d’origine
  • Cette approche apportait un gain de performances considérable par rapport à une émulation basée sur un interpréteur
  • On peut voir le x86-32 comme du bytecode et l’émulateur comme un compilateur JIT

Le code problématique : initialisation de 64 Ko de mémoire de pile

  • Un programme devait allouer environ 64 Ko de mémoire sur la pile et les initialiser
  • La méthode standard consistait d’abord à effectuer un sondage de pile afin de vérifier que 64 Ko de mémoire pouvaient être utilisés
  • Ensuite, on soustrayait 65 536 au pointeur de pile, puis on initialisait la mémoire avec une petite boucle très serrée

Le déroulage de boucle excessif du compilateur

  • Le compilateur qui a compilé ce code n’a pas généré de boucle pour initialiser chaque octet
  • À la place, il a déroulé la boucle en 65 536 « instructions d’écriture d’un octet en mémoire » distinctes
  • Chaque instruction avait une longueur de 4 octets
  • Au final, il fallait 256 Ko de code pour initialiser 64 Ko de données

La réponse de l’équipe de l’émulateur

  • L’équipe de l’émulateur a ajouté au traducteur un code spécial permettant de détecter cette fonction
  • Une fois détectée, la fonction était remplacée par une courte boucle assurant un comportement équivalent
  • Ce traitement consistait, au lieu de traduire tel quel le code du programme d’origine, à transformer pendant l’émulation un motif de code inefficace en une forme plus compacte

1 commentaires

 
GN⁺ 4 시간 전
Avis sur Lobste.rs
  • J’ai bien aimé le commentaire qui expliquait le loop unrolling à Raymond Chen

    • Ce commentaire avait peut-être été écrit non seulement pour l’auteur du blog, mais aussi pour les lecteurs en général
      parmi les personnes qui lisent ce genre de billet, tout le monde ne connaît pas forcément tout le contexte, et beaucoup apprécient qu’on leur donne des pistes pour en apprendre davantage
    • Je n’ai pas vu ce commentaire, il a sans doute été supprimé. Cela dit, Raymond Chen reste Raymond Chen, une véritable légende
      https://joelonsoftware.com/2004/06/…
    • Ça me rappelle l’époque où, sur Slashdot il y a des décennies, quelqu’un avait essayé d’expliquer un sujet Perl à larry@wall.org
  • Ça devait être sur Alpha. Il y a eu énormément de travail sur l’émulateur x86 pour cette plateforme