- Le RV64 DynaRec de Box64 est passé, en un an, d’un niveau où il faisait tourner de simples jeux Linux natifs à l’exécution de The Witcher 3 sur un PC RISC-V
- Le déclic a été la possibilité d’utiliser des cartes graphiques AMD : les contraintes liées à OpenGL ont diminué, permettant de tester davantage de programmes x86 et de corriger plus de bugs
- Le backend RV64 implémente moins d’instructions x86 que le backend ARM64, et les instructions AVX restent encore un gros chantier côté RISC-V
- RISC-V manque d’extraction/insertion de plages de bits et d’instructions atomiques sur 16 octets, ce qui rend le coût de traduction de l’émulation x86 plus élevé que sur AArch64 ou LoongArch64
- The Witcher 3 s’exécute réellement avec box64, atteignant jusqu’à 15 fps en jeu et la pleine vitesse dans le menu principal
Jusqu’à l’exécution de The Witcher 3 sur RISC-V
- Il y a un an, le RV64 DynaRec ne pouvait faire tourner que des jeux Linux natifs relativement faciles à exécuter, comme Stardew Valley ou World of Goo
- À l’époque, il y avait deux principaux goulets d’étranglement
- De nombreux bugs du DynaRec subsistaient, pendant l’ajout rapide d’instructions x86_64 au backend RISC-V
- Les GPU intégrés IMG du VisionFive 2 et du LicheePi 4A ne prenaient en charge qu’OpenGL ES, et pas OpenGL
- gl4es permettait d’obtenir une prise en charge partielle d’OpenGL et de lancer des jeux comme Stardew Valley, mais ce n’était pas suffisant pour des jeux Linux plus lourds ni pour des jeux Windows classiques
- Le Milk-V Pioneer de Sophgo est un PC RISC-V à 64 cœurs, doté d’un emplacement PCIe permettant d’installer une carte graphique
- Un autre contributeur, xctan, a trouvé comment « connecter » une carte graphique AMD au VisionFive 2 via l’interface M.2
- Avec la possibilité d’utiliser des cartes graphiques AMD, l’éventail de programmes x86 testables s’est élargi, entraînant de nombreuses corrections de bugs du RV64 DynaRec et l’ajout massif d’instructions x86
- Résultat : The Witcher 3 a fonctionné dès son premier lancement
État actuel du RV64 DynaRec
- Le jeu d’instructions x86 est très vaste, et l’ampleur de l’implémentation varie selon les backends
- Le backend ARM64 implémente au total plus de 1 600 instructions x86
- Le backend RV64 implémente environ 1 000 instructions x86
- Parmi elles, plus de 300 sont de nouvelles instructions AVX prises en charge, qui ne sont pas encore du tout implémentées sur RISC-V
- RISC-V est aussi désavantagé en matière de performances pour l’implémentation des instructions SSE
- Le backend RV64 implémente les instructions SSE sous forme d’instructions scalaires
- AArch64 utilise l’extension Neon, et LoongArch64 l’extension LSX
- À cause de cette différence, ses performances sont nettement inférieures à celles des deux autres backends
- RISC-V dispose d’une extension vectorielle, RVV
- Le Milk-V Pioneer prend en charge l’extension xtheadvector, une variante de RVV 0.7.1
- Le SoC SpacemiT K1/M1 prend en charge la version ratifiée RVV 1.0
- Les Banana Pi F3 et Milk-V Jupiter, équipés de ce SoC, sont disponibles à l’achat
- box64 a récemment ajouté une prise en charge RVV de base, ainsi que l’implémentation de quelques instructions SSE courantes
- Le travail sur RVV en est encore à un stade très précoce et n’apporte donc pas encore d’amélioration de performances à ce jour
Les instructions RISC-V particulièrement manquantes pour l’émulation x86
- Du point de vue de l’émulation x86, RISC-V est l’une des moins expressives des trois architectures prises en charge
- Par rapport à AArch64 et LoongArch64, il lui manque des instructions pratiques, ce qui nécessite davantage d’instructions pour émuler un même comportement
- Deux fonctionnalités sont particulièrement importantes et absentes
- La possibilité de sélectionner une plage de bits précise dans un registre et de la copier dans un autre registre
- La possibilité d’insérer certains bits d’un registre dans une plage précise d’un autre registre
- LoongArch64 et AArch64 disposent d’instructions correspondant à ces usages
- LoongArch64 utilise
BSTRPICK.DetBSTRINS.D - ARM64 utilise les opcodes
UBFXetBFI
- LoongArch64 utilise
- RISC-V ne propose pas d’instruction équivalente, ni dans ses extensions officielles ni dans ses extensions de fournisseurs
Ce que montre l’exemple ADD AH, BL sur le coût de traduction
- L’ISA x86 a tendance à préserver les bits non modifiés, ce qui rend les manipulations de registres partiels importantes
- Dans le cas de
ADD AH, BL, box64 doit effectuer les opérations suivantes- Extraire l’octet de poids faible de
RBX - L’ajouter au deuxième octet de poids faible de
RAX - Réinsérer le résultat dans le deuxième octet de poids faible de
RAX - Conserver tels quels les autres octets de
RAX
- Extraire l’octet de poids faible de
- Sur LoongArch64, cela peut être implémenté de façon simple et intuitive avec
BSTRPICK.D,ADDetBSTRINS.D - Sur RISC-V, la même opération nécessite de combiner décalages, masques,
AND,OR, etc., soit 10 instructions - Ce cas n’est pas isolé : x86 comporte de nombreuses instructions de forme similaire, ce qui rend l’implémentation RISC-V plus laborieuse
La contrainte des instructions atomiques sur 16 octets
- x86 dispose d’instructions avec préfixe LOCK pour les opérations atomiques lock-free
- box64 les émule principalement au moyen de séquences LR/SC
- LR/SC signifie Load-Reserved / Store-Conditionally
- Par exemple,
LOCK ADD [RAX], RCXest généré sous la formeLR.D,ADD,SC.Det branchement conditionnel
- Si l’adresse
RAXn’est pas alignée, cela devient plus complexe, mais cette approche fonctionne généralement bien - Le problème est
LOCK CMPXCHG16B- Cette instruction compare
RDX:RAXavec les 16 octets en mémoire - Selon la condition, elle échange
RCX:RBXavec cette adresse mémoire
- Cette instruction compare
- AArch64 et LoongArch64 disposent de certaines instructions atomiques sur 16 octets utilisables pour l’implémentation
- RISC-V ne possède pas d’instruction équivalente, ce qui empêche une implémentation aussi complète que sur les autres architectures
- De nombreux programmes, y compris des jeux Unity, utilisent
LOCK CMPXCHG16B
Résultats d’exécution réels
- Malgré les contraintes restantes, The Witcher 3 tourne sur RISC-V avec box64
- Les performances atteignent jusqu’à 15 fps en jeu
- Dans le menu principal, il fonctionne à pleine vitesse
- Pour un résultat obtenu sur une machine qui n’a pas été conçue pour faire tourner des jeux AAA, le niveau est tout à fait honorable
1 commentaires
Avis sur Hacker News
Je suis curieux, en tant que personne qui ne travaille pas côté puces : quand on développe des logiciels ciblant RISC-V, qu’est-ce qu’un ingénieur logiciel doit faire différemment ?
Je me demande si la taille des exécutables augmente au point de devoir optimiser agressivement la localité du cache, et s’il existe des types de logiciels, comme les jeux ou les serveurs web, qui conviennent mieux à CISC ou à RISC.
Il n’y a pas grand-chose à changer fondamentalement dans l’approche logicielle ; par rapport à x86-64, la plus grande différence est qu’il y a 32 registres, ce qui permet de conserver davantage de valeurs intermédiaires avant de devoir les pousser sur la pile, mais ARM en a aussi 32, donc c’est similaire. En général, sauf à faire des micro-optimisations, il n’y a pas vraiment à s’en soucier.
Plus en détail, l’extension vectorielle (V/RVV) ne fait pas partie de l’ISA rv64gc de base, donc selon la cible on peut ne pas bénéficier des optimisations SIMD ; le popcount et le comptage des zéros de tête/de fin ne sont pas non plus dans rv64gc de base et nécessitent Zbb. De plus, une sélection sans branchement comme
a ? b : cnécessite 4 à 5 instructions en rv64gc de base, 3 instructions avec Zicond, alors qu’elle peut se faire en 1 seule sur x86-64 et aarch64.Les profils RISC-V résolvent en partie les deux premiers problèmes. Par exemple, Android exige rva23, qui requiert RVV, Zbb, Zicond, etc. Mais si une distribution Linux cible rva20/rv64gc, ces extensions deviennent de fait inutilisables pendant longtemps dans du code précompilé sans dispatch dynamique. x86-64 a un problème similaire, mais ARM a beaucoup moins d’extensions, donc c’est moins marqué ; SVE est la grande exception, mais il n’est pas encore largement pris en charge.
La plus grande différence est le modèle mémoire faible, mais c’est aussi une caractéristique de la plupart des architectures non x86 comme ARM, et le code ne devrait de toute façon pas dépendre d’un modèle mémoire fort.
Pour des raisons historiques, la densité du code exécutable de x86 n’est pas très bonne, donc la taille des exécutables n’augmente pas autant qu’on pourrait le penser. RISC-V avec l’extension d’instructions compressées et ARM 32 bits avec l’extension Thumb sont assez denses.
Ce qui compte, ce n’est pas CISC contre RISC, mais l’existence et la qualité des instructions vectorielles et des extensions cryptographiques. L’encodage/décodage vidéo dépend fortement des instructions vectorielles pour obtenir de bonnes performances, et le chiffrement complet du disque ou le hachage peuvent bénéficier d’instructions dédiées qui accélèrent des algorithmes précis comme AES ou SHA256.
L’enjeu est surtout de s’affranchir des brevets ARM et de prendre un nouveau départ en s’appuyant sur les leçons apprises.
Ça me rappelle un Russe célèbre qui a fait tourner Atomic Heart sur Elbrus 8S.
Elbrus dispose d’un traducteur natif et, à ma connaissance, il est plutôt correct. Atomic Heart tournait autour de 15 à 25 fps, donc c’était plus ou moins jouable.
L’article manque un peu d’explications « de base ». Je pensais que le jeu avait été lancé avec quelque chose comme un port de Wine, mais en réalité on dirait qu’ils ont implémenté d’une certaine manière l’ISA x86_64 sur une puce RISC-V.
Ce serait bien si quelqu’un pouvait expliquer davantage cette partie.
C’est bien un émulateur, mais comme certaines bibliothèques « système » comme libc, libm, SDL ou OpenGL utilisent leurs versions natives, il est facile à intégrer avec la plupart des applications et, dans certains cas, les performances peuvent être étonnamment élevées. Wine peut aussi être compilé nativement et exécuté.
C’est un résultat impressionnant. Cela représente une énorme quantité de travail et, dans certains cas, on dirait qu’ils touchent aux limites de RISC-V.
Les instructions de collecte/dispersion de bits devraient probablement être ajoutées sous forme d’extension.
Dans le contexte de l’émulation x86, il est intéressant que, parmi les trois architectures prises en charge, RISC-V soit la moins expressive.
En cours d’histoire de l’informatique, on nous a appris que RISC signifiait « ordinateur à jeu d’instructions réduit », mais quand on regarde les propositions de profils RISC-V ou les articles récents, on voit souvent des formulations du type « il suffit de quelques instructions de plus pour atteindre l’équivalence fonctionnelle ». Je comprends que RISC-V soit, pour beaucoup, une alternative pratique à d’autres plateformes, mais je me demande aussi si cela signifie que le rêve du RISC est mort.
D’après mes souvenirs de lecture de la spécification RISC-V, elle était assez stricte sur le fait de ne pas ajouter d’instructions “combo”, puisque les séquences d’instructions courantes peuvent être fusionnées dans le front-end.
Les manques de RISC-V par rapport à x86/ARM semblent moins venir d’un fondamentalisme RISC que du fait que la spécification est partie de puces embarquées très basiques, puis a ajouté au fil du temps des extensions pour CPU applicatifs. Le RV32I de base n’a même pas la multiplication entière. Malheureusement, il a fallu beaucoup trop longtemps pour trancher les débats sur la manipulation de bits et les extensions SIMD/vectorielles, ce qui a créé les trous fonctionnels dont il est question aujourd’hui.
En contrepartie, certaines instructions pratiques pour les hautes performances sont exclues.
Un pipeline simple a aussi l’avantage de consommer moins de ressources d’ingénierie pour les équipes qui conçoivent des processeurs hautes performances, ce qui leur laisse plus de temps pour l’optimisation.
Le RISC est globalement une philosophie de simplification, mais son degré varie. MIPS est aussi simplifié que RISC-V, tandis qu’ARM et POWER sont plus pragmatiques, et ne semblent pas avoir de gros problème à rivaliser avec x86 dans le haut de gamme.
Le marché des processeurs ne se limite pas à l’exécution d’applications : il existe de nombreuses niches, comme l’embarqué ou les accélérateurs. Je suis assez pessimiste sur RISC-V dans la niche spécifique des cœurs applicatifs, mais, plus largement, son potentiel est important ; il pourrait dominer certaines niches commerciales, et c’est un excellent outil pour l’enseignement et la recherche.
Les caractéristiques classiques du RISC sont que la plupart des instructions de manipulation de données n’opèrent que sur des registres, que les instructions mémoire se limitent généralement à des load/store vers des registres, et qu’il faut donc beaucoup de registres. Comme il faut manipuler directement la pile pour passer des paramètres, on construit aussi soi-même la pile ; et, sans instructions CALL/JSR, on les implémente avec des instructions de base qui font directement des load/store dans le registre du pointeur d’instruction. L’encodage des instructions est prévisible et toutes les instructions ont la même taille. Plusieurs architectures RISC avaient aussi un registre qui lit toujours 0 et dans lequel on ne peut pas écrire, utilisé pour mettre des valeurs à 0.
Cette approche a fonctionné, mais ensuite l’exécution dans le désordre et le SIMD en ont réduit l’importance. Le flux brut d’instructions ressemble davantage à une déclaration du chemin vers le résultat souhaité ; cela ne veut pas dire que le CPU l’exécute réellement tel quel. Derrière, il y a l’exécution spéculative, la prédiction de branchements et le renommage de registres. Le SIMD ressemble plutôt à un grand espace de registres et à des instructions qui opèrent sur toutes les valeurs qu’il contient. Au final, l’exécution dans le désordre et le SIMD ont pris le dessus.
En théorie, si l’on compilait le code source original pour RISC, on obtiendrait un binaire complètement différent, et ces instructions précises pourraient ne pas être nécessaires.
En pratique, il est peu probable que quelqu’un compile réellement ces jeux pour RISC-V.
La capture d’écran affiche 31 Go de RAM, ce qui dépasse clairement la configuration maximale de la carte de développement mentionnée. Est-ce qu’ils utilisent autre chose ici ?
Aujourd’hui, il vaudrait mieux utiliser l’une des options récentes avec plusieurs cœurs plus rapides implémentant RVA22 et RVV 1.0.
Est-ce que c’est 86Box ? Ça m’a amusé de repenser à l’époque où j’avais acheté un Amstrad PC1512.
C’était devenu bien plus amusant après avoir ajouté deux cartes disque dur de 500 Mo et une extension mémoire de 128 Ko pour atteindre 640 Ko. Au début, il n’avait que deux lecteurs disquette de 360 Ko, puis j’ai ajouté une carte disque dur de 32 Mo quelques années plus tard. J’avais aussi Borland TurboPascal et Zortech C. C’était une belle époque.
Mais je me souviens quand même de l’époque de l’Amstrad PC1512.
Je me demande si l’on verra un jour un système embarquant quelques gros CPU RISC-V et, à côté, un « GPU » implémenté avec une grappe de petits CPU RISC-V.
Ce serait probablement avec des capacités vectorielles adaptées ; et, question annexe, je me demande aussi si une approche de vecteurs classique plutôt que du SIMD packed pourrait être utile dans un GPU.
Parmi les réussites techniquement impressionnantes de Witcher 3, il y a aussi eu le portage Switch, qui tournait vraiment bien.
Cela montre tout ce que l’optimisation peut accomplir, et combien de ressources sont gaspillées sur PC à cause d’une mauvaise optimisation.
Ce n’est pas une comparaison à armes égales, et comme ce qui est affiché à l’écran diffère fortement, il est difficile de conclure à une mauvaise optimisation sur PC.
Ce serait bien que ce type de retours au niveau de l’ISA parvienne aux personnes côté RVI
En vérifiant hier [1], l’exemple de l’article peut déjà se faire avec 4 instructions RISC-V. Cela dit, ce n’est pas forcément évident à trouver
# a0 = rax, a1 = rbxslli t0, a1, 64-8rori a0, a0, 16add a0, a0, t0rori a0, a0, 64-16[1] https://www.reddit.com/r/RISCV/comments/1f1mnxf/box64_and_ri...
En fait, l’absence d’extraction de champs de bits est une erreur tellement évidente que c’est mon exemple préféré pour montrer à quel point l’ISA RISC-V est absurde. Le deuxième, c’est l’absence de modes d’adressage sensés
Certaines conceptions RISC-V plus abouties implémentent même des instructions custom pour cela. Par exemple, BEXTM dans Hazard3 : https://github.com/Wren6991/Hazard3/blob/stable/doc/hazard3....