Aller au contenu

Portages natifs et gains mesurés

Catalogue des optimisations qui remplacent un bout de code x86 traduit par une implémentation native ARM équivalente (voir le mécanisme dans la documentation de winx86, page Point d'extension). Chaque ligne précise le niveau de validation du chiffre cité (voir Validation à trois niveaux) — un chiffre qemu n'est jamais présenté comme un résultat console. Chiffres tirés des campagnes de mesure internes du projet (rapports datés, non publiés dans ce dépôt) ; ce qui suit en est la synthèse.

Aucune valeur absolue ne traverse une campagne

Le binaire change, la console dérive thermiquement (1,5-10 %). Les pourcentages ci-dessous viennent de mesures A/B/A entrelacées sur un binaire identique, jamais de deux dates différentes comparées entre elles.

Portages ayant produit un gain confirmé sur console

Portage Gain mesuré Niveau Détail
Boucle de cellules native (NATIVECELLLOOP) +16,1 % Console (A/B/A/B, binaire identique) Traversées ÷2, blit ÷7.
D2_CELLOPT (masque d'optimisations dans la boucle native) +9,3 % Console Moitié moins de travail par cellule.
D2_CELLPAR=1 (parallélisme fork-join, 1 fil ouvrier) +12 % Console D2_CELLPAR=2 = 0 % (USER_2 partagé, pas de second cœur disponible).
D2_CALLRET +6,8 % console / 0 % qemu Console (4 passes entrelacées, binaire unique) Cas d'école : un gain réel que qemu ne voit pas du tout.
fastmmu + D2_BUDGETTAIL +2,8 % Console Sortir l'ADD de base du chemin critique du dynarec.
D2_CSINTRIN + WX86_B5 (intrinsèque d'horloge) +1,4 % Console Intrinsèque d'horloge réécrit pour sortir du chemin critique.
Sonde fog_raise_snap désarmée +4 à 7 % Console La sonde elle-même coûtait ce qu'elle mesurait.
Ring GPU — soumission asynchrone (D2_GXMASYNC=2) +52 % (28,8 → 43,7 img/s banc) Console Le GPU met 11 ms/soumission ; le mode async recouvre ce temps.
Ring GPU — effacement plat (D2_GXMCLEAR=plat) +10,5 % (−2,5 ms) Console Effacement du framebuffer simplifié.
Ring côté CPU — vue hôte + sommets rapides +11,5 % (43,75 → 48,78 img/s banc) Console Parcours du ring ÷2,3.
Hissage de la résolution TLS hors de CpuBox86::run() +3,03 % au banc menu, +8,6 % en jeu en ligne réel Console (A/B/A/B entrelacé + garde-fou : travail invité identique à 0,1 % près) Le gain suit la densité de prises de GIL (190/image au menu contre 2880/image en jeu) — un pourcentage mesuré au menu ne doit jamais être publié comme un gain de jeu.
Regroupement entrée/sortie de trap (trap_retaddr+trap_epilogue, commit e13edbd) +13,2 % annoncé au commit Chiffre de commit, cité tel quel dans une correction ultérieure — pas de document de campagne dédié retrouvé pour ce chiffre précis, à prendre avec cette réserve. Déjà en place aujourd'hui (pas un knob optionnel).
NATIVELIGHTMAP (pose d'une lumière dynamique) −21 % sur la passe lumière (1,20 → 0,95 ms/image), soit −2,6 % du dessin total qemu (oracle croisé D2_LIGHTMAPVERIFY, 51 200 poses, 0 repli, 0 divergence) — verdict console non pris Jamais mesuré sur console.
Cumul de la nuit du 04-05/09, banc déplafonné Témoin 22,5-23,1 → 28,1-28,6 img/s (+22 %) Console, campagne entrelacée de 20 passes Cumul de plusieurs des portages ci-dessus.
Cumul GDI → Glide asynchrone 22,5 → 55,7 img/s (+96 %) banc ; 25 pas/s + 60 img/s lissés en jeu réel Console Cumul du passage au rendu GPU asynchrone (ring + soumission async).

Portages réfutés ou sans effet mesurable — gardés comme mise en garde

Portage Résultat Niveau Pourquoi c'est instructif
D2_MEMINTRIN (memcpy/memset natifs) Mécanisme prouvé correct (4,4 M d'appels, 0 divergence) mais 0 % sur console Console Le CRT invité vectorise déjà ses gros transferts (NEON) ; le chemin Glide fait 90 appels/image contre ~1 100 sous GDI — le levier avait disparu avec le changement de renderer.
D2_FORWARD Inerte (+0,6 % = bruit) Console Hypothèse « blocs plus courts » jamais testée.
D2_NOPEND (levier L4) Réfuté, plafond 0,002 % Console Le document original visait le mauvais site de code (0 appel réel au lieu du site supposé).
Report du RLE sur un fil dédié FAIL sur trois modes ; le mode qui passait trois jours plus tôt ne passe plus qemu (oracle) « Prouver l'oracle avant de conclure » — le PASS fondateur ne s'était jamais rejoué depuis.
Quatre hypothèses de cache d'image Toutes réfutées (0 image identique sur 4 000) qemu (D2_CACHEPROBE) A trouvé autre chose à la place : l'option vidéo Perspective absente du registre coûtait +14,2 % de blocs invités.
PMU matériel (compteurs de cycles Cortex-A9) Abandonné Console Le noyau Vita remet le registre d'accès à zéro à chaque changement de contexte ; demanderait de patcher l'ordonnanceur système.
Quinze leviers dynarec, bilan de campagne Un seul gain dans toute l'histoire du dynarec (D2_CALLRET, +6,8 %) Console « le dynarec n'est pas le levier », le gain est dans le portage natif de code invité, pas dans la traduction elle-même.

Méthode de sélection d'un portage qui rapporte

Ce que la campagne a appris se formalise ainsi : gain ≈ N × (T_invité − T_natif − 69 ns)69 ns est le coût mesuré du passage x86→natif lui-même (le prix d'un aller-retour, même pour un intrinsèque reconnu à la traduction plutôt qu'un vrai trap). La bonne cible n'est pas la fonction la plus chaude au profil — c'est un appelant avec un grand nombre d'appels (N) qui absorbe tout un sous-arbre en une seule traversée. C'est exactement le patron de la boucle de cellules : un seul site d'appel produit la totalité des ~1600 appels au blit de cellule par image.

Config de jeu retenue

tools/bancs/env_jeu_glide.txt (déposée sur console, choisie par l'utilisateur) — c'est la combinaison de tous les leviers confirmés ci-dessus :

D2SCHED=native D2_SELECTBLOCK=1 NATIVECELLLOOP=1 D2_CALLRET=1
D2_NOPUMPWAIT=1 D2_LAZYSEEK=1
D2_CELLOPT=63 D2_MMUSTACK=3 D2_MMUFOLD=1 D2_BUDGETTAIL=1 D2_CSINTRIN=1 WX86_B5=1
D2ARGS=game.exe -3dfx  D2_GLIDERING=1 D2_GLIDEGXM=1 D2_GXMASYNC=2 D2_GXMCLEAR=plat
WX86_YIELD=2  D2_ONEDRAW=2  D2_REPLAY60=1

(D2_NOCAP n'y figure pas — c'est un outil de banc qui débraye les limiteurs d'images internes du jeu, pas un réglage à garder en jeu réel.)