Benchmark des différents compilateurs C pour le 6502Choisir un compilateur C pour le 6502 n'est pas si facile. Chacun à son lot de forces et faiblesses. On peut chercher la vitesse, la facilité d'utilisation, le respect du standard, ou vraiment n'importe quoi.
Quand j'ai dû faire ce choix pour Super Tilt Bro., la vitesse était le dernier de mes soucis. Cependant, en discutant avec d'autres developpeurs, le sujet revenait toujours sur le tapis. Ça sera donc notre principal focus : quel compilateur produit le code le plus rapide ?
Quelques petits bouts de code seront comparés. À chaque fois on expliquera ce que fait le code et pourquoi il est pertinent de le bencher.
Ensuite, je parlerai de mon expérience à utiliser chacun de ces compilateurs. Leurs forces et faiblesses au-delà de la performance. Forcément, c'est subjectif, j'essaierais de rester factuel mais je ne promets rien.
Enfin, une petite section "comment choisir son compilateur" conclura cet article. En espérant que ça serve à quelqu'un.
C'est partit !
Les candidats au - très convoité - titre de meilleur compilo ?cc65 est le plus utilisé. Il bénéficie d'une énorme suite d'outils, est activement maintenu, se bonifie avec le temps et à une grande communauté d'utilisateurs. Ceci dit, il a la réputation de produire un code particulièrement lent... on verra bien

vbcc c'est le compilateur tendance. Moins utilisé que le roi, il a la réputation de produire un code bien meilleur.
KickC est le petit jeune plein d'avenir. Est-ce qu'il a déjà de quoi jouer dans la court des grands ?
6502-gcc est un sombre inconnu. Personne ne le maitrise vraiment, le projet semble mort, les instructions d'installation sont impénétrables,... Nous allons tenter de faire la lumière sur ses inavouables secrets !
6502-gcc à deux options de compilation très différentes :
- "-O3" optimise le code pour être rapide
- "-Os" optimise le code pour être petit
DécompressionCe benchmark est issu de la vraie vie. Il s'agit d'une fonction prototypée en C pour Super Tilt Bro. avant d'être convertie en assembleur à cause de mauvaise performances. Les données compressées sont aussi celles d'origine.
La fonction fouille dans un tas de données compressée pour en extraire quelques octets. C'est de la grosse logique qui boucle, passant son temps à lire la mémoire.
En rouge on a le temps d'exécution, en bleu la taille du code.
Ça saute aux yeux, cc65 est fidèle à sa réputation. Le code généré est extrêmement lent.
Le gagnant est 6502-gcc, avec une avance correcte sans être confortable. KickC à un très léger avantage sur vbcc.
Fun fact : plus un compilateur est populaire, pire est sa performance sur ce test.
MemcopyLà, c'est la tâche la plus commune au monde : on copie des octets d'un endroit à l'autre de la mémoire. Il est toujours judicieux de comparer les compilateurs sur ce qu'on leur demandera de faire le plus souvent.
Il y a deux versions de ce test :
- Le normal est une boucle qui copie les données
- le compilateur peut déterminer les adresses et la taille des données, ainsi il peut optimiser pour ce cas particulier.
- Le "no-inline" passe par une fonction indépendante
- le compilateur est forcé de générer un code générique de copie d'octets
Les compilateurs sont triés : du plus lent au plus rapide
cc65 est fidèle à son poste de dernier.
KickC confirme être un poil plus rapide que vbcc. À noter l'absence de KickC dans la version "no-inline" : KickC fonctionne toujours en connaissant tous les sources du projet, impossible de l'empêcher d'inliner.
6502-gcc prend un coup dur ici. Pire, en menant d'autres tests on s'aperçoit que ses performances varient énormément en changeant quelques détails dans le code.
Aussi, avez-vous remarqué que "-O3" est terriblement mauvaise dans la version inline ? gcc (le "vrai" ainsi que 6502-gcc) fonctionne en deux étapes : le front-end optimise le code C, puis le back-end génère du code machine. Le front-end de 6502-gcc est le même que celui de gcc, il optimise comme un dieu, puis vient le back-end. Le back-end 6502 manque sévèrement d'amour et ça se voit ici. Dans notre cas, le front-end comprend ce qu'on fait et dis simplement au back-end "copie 200 octets de $400 vers $200". Le back-end devrait implémenter ça de la façon optimale, mais il se contente d'appeler "jsr memcpy", qui est la fonction standard de copie et n'est pas DU TOUT adaptée à un CPU 8 bits.
Le meilleur JDR au monde !Disclaimer : ce bench a été pensé pour tirer avantage du front-end extraordinaire de 6502-gcc.
C'est un moteur de JDR avec tout plein d'abstractions. Il y a des structs, des fonctions pour que le héro frappe, des fonctions pour que le monstres reçoivent un coup, le héro porte une arme,...
La fonction benchée initialise l'état du jeu, joue un tour et retourne. Du coup, bien qu'il y ait plein d'appels de fonctions, de calculs de point de vies, et d'aller-retours, finalement la fonction doit simplement mettre la mémoire dans l'état du jeu après un tour.
cc65... ça devient lassant. Bon, ce test n'est définitivement pas pour lui. cc65 ne fait strictement aucune optimisation de haut niveau alors que bench test la qualité des optimisations de haut niveau.
KickC est un meilleur vbcc côté performances. Rien de nouveau.
6502-gcc ne déçoit pas. Son front-end est l'un des meilleurs au monde (sans mentir ni exagérer) et là, il voit l'arnaque. Le code généré est une simple succession de "LDA <constante> : STA <mémoire>" pour écrire en mémoire l'état de la partie après un tour de jeu.
Du code pour cc65Avez-vous lu le superbe article "Advanced optimizations in CC65" ? Vous le trouverez ici :

ilmenit/CC65-Advanced-OptimizationsGitHubHow to optimize C code for CC65 compiler. Contribute to ilmenit/CC65-Advanced-Optimizations development by creating an account on GitHub. Opération sauvez le soldat cc65 ! On va bencher un code spécialement fait pour cc65, codé par un expert. Ça devrait le faire, hein ?
Le code est comparable au moteur de JDR : c'est un moteur de JDR plein d'abstractions inutiles. La différence est qu'il boucle 100 fois et affiche les personnages à l'écran à chaque fois, donc pas moyen de simplement réduire ça à une suite de LDA+STA, il va falloir calculer des trucs.
Sur cette base, Ilmenit applique différentes optimisations jusqu'à ce que le code aille vraiment vite.
On benchera deux versions : la première, sans optimisation, et la dernière, avec toutes les optimisations.
KickC a disparu et vbcc en piteux état !
KickC a simplement trop de limitations pour compiler ce code. Entre autres il refuse de générer du code pour un modulo ou une division.
vbcc a généré une boucle infinie sur la version non-optimisée. Il a aussi souffert de mon manque d'expérience pour l'intégrer à mon outil (d'où l'absence d'info sur la taille du code.)
6502-gcc a dévoilé un sal bug. Les variables globales sont visiblement mises dans un segment au hasard. L'assembleur généré a dû être fixé à la main pour le faire tourner.
Donc, même sans lire les graphiques : cc65 a gagné ! Il est le seul à réussir à compiler ce code !
Il est intéressant de voir que 6502-gcc n'est pas particulièrement bon sur la version non-optimisée. Après tout ce qu'on a dit sur la puissance de ses optimisations de haut niveau ! Le truc c'est que le code ne lui permet pas d'inliner. En C, quand on déclare une fonction sans la précéder de "static", elle doit être accessible aux autres unités de compilation et donc être complètement utilisable : avec passage de paramètres par la pile et tout le tintouin. Bien sûr ça ne change pas grand-chose pour cc65 qui de toute façon n'inline rien.
Sur la version optimisée, il n'y a pas de grande différence. vbcc est derrière mais souffre peut-être de mon inexpérience et de mesures grossières. Le code a été optimisé pour être facile à compiler, rien d'étonnant à ce que tous les compilateurs est à peu-prêt le même résultat.
Et ce "???", qu'est-ce que c'est ? Si vous n'avez pas encore lu "Advanced optimizations in CC65" (Ayez honte!), vous ne vous rendez pas compte à quel point le code optimisé est illisible. Toutes les astuces possibles et imaginable y sont passée, même l'auteur ne recommande pas ça dans la vraie vie.
"???" c'est "6502-gcc -Os", sur un code avec seulement deux des 12 optimisations et surtout le mot clé "static" utilisé à bon escient. Cette version ressemble beaucoup à la version non-optimisée et la pénalité de performance est quasi nulle.
C'est pourquoi je pense que les optimisations haut niveau sont importantes. Elles permettent d'écrire en C, sans trop se soucier de l'assembleur généré. On laisse les astuces bas niveau au compilateur et on se concentre sur la logique.
Les performances, ce n'est pas toutOk, côté performances il semblerait que cc65 est loin derrière, vbcc et KickC sont quasiment ex aequo et 6502-gcc oscille entre l'excellence et la catastrophe.
À vrai dire, les performances, ce n'est pas ce qui devrait compter le plus quand on choisit un compilateur C (pour le 6502.) Apprenez l'assembleur et vous aurez toujours moyen d'injecter des performances là où ça compte.
Voici un petit résumé des avantages et inconvénients de chaque compilateur :
cc65
Pros
- Rock-solid et battle-tested, il ne vous claquera pas entre les doigts.
- En développement actif
- Plein de ressources disponibles pour apprendre
- L'une des suites d'outils les plus complètes
Cons
- Performances (sérieusement, c'est son seul défaut)
vbcc
Pros
- Performances acceptables
- Il a des utilisateurs actifs
- La documentation est super complète
- Suite d'outils bien fournie (assembleur, linkeur, fichiers de configurations,...)
Cons
- Pire licence possible (vraiment je recommande de l'éviter à cause de ça)
- Parfois buggué
KickC
Pros
- Bonnes performances
- Difficile à intégrer avec d'autres outils (il veut compiler tout un projet d'un coup)
- Développement actif (et la base est bonne, le futur s'annonce prometteur)
Cons
- Respect partiel du standard C (même "const" est mal supporté)
- La compilation est lente
- Les erreurs de compilation sont cryptiques
6502-gcc
Pros
- Respecte le standard C à la lettre
- Optimisations haut niveau qui profitent de la puissance de gcc
- Génère de l'assembleur pour ca65 (et donc compatible avec tous les outils de cc65)
- Erreurs et warnings lisibles, et qui peuvent même remonter de vrais bugs dans votre code
Cons
- Développement inactif
- Buggué
- Qualité du code généré très variable
Ok, donc lequel choisir ?Comme toujours, ça dépend ! Qui êtes-vous ?
Êtes-vous un développeur expérimenté, habitué à l'un de ces compilateurs ? Restez dessus, vous le maîtrisez déjà, les autre n'apportent que peu d'avantages.
C'est votre premier projet C pour le 6502 ? Il vous faut cc65, c'est le plus mature et vous trouverez de l'aide quand vous en aurez besoin.
Il vous faut des optimisations de haut niveau, et un truc qui marche sans se prendre trop la tête ? KickC est fait pour vous.
Il vous faut des optimisations de haut niveau, et avez tendance à exploiter les détails du standard C ? Vous être prêts à vous faire vos propres outils au besoin ? 6502-gcc est le compilateur idéal.
Non. Je ne recommanderais jamais vbcc. Sa licence est horrible. Les sources ne sont pas disponibles et vous ne pouvez pas l'utiliser pour "usage commercial" (sans définition spécifique, ni d'info si ça s'applique aussi aux programmes générés.) En prime, il intègre divers outils et bibliothèques qui ont chacun leur propre licence. Pour faire les choses bien, vous devrez au moins lire trois licences avant de savoir si ce que vous avez en tête est autorisé.
Un dernier motLes benchmarks c'est joli et montre un classement clair, mais ce n'est jamais parfait. Ces benchmarks en particulier, c'est ma première expérience avec la plupart de ces compilateurs, j'ai possiblement raté des choses. Si vous voulez jouer les apprentis sorciers vous-même, l'outil utilisé est disponible ici :
https://github.com/sgadrat/6502-compilers-bench Ça prend un fichier C et sort les infos de rapidité ainsi que l'assembleur généré.
En espérant que cette petite recherche aide quelqu'un un jour. Mais n'oubliez pas, en développement rétro, l'important c'est de s'amuser (au sens geek du terme.)