14 points par GN⁺ 2025-12-30 | 13 commentaires | Partager sur WhatsApp
  • Le runtime Mono utilisé par Unity affiche des vitesses d’exécution nettement inférieures à celles du .NET moderne, avec des cas où le même code C# présente un écart pouvant aller jusqu’à 15x
  • Dans du vrai code de jeu, l’exécution Unity basée sur Mono prend 100 secondes, contre 38 secondes pour le même code sous .NET, ce qui affecte fortement l’efficacité du débogage et des tests
  • Même en mode Release, Mono met 30 secondes contre 12 pour .NET, ce qui maintient un écart de performance supérieur à 2,5x même dans un environnement optimisé
  • La cause vient du JIT inefficace de Mono, de ses échecs d’inlining et de copies mémoire excessives, en contraste avec les optimisations modernes du JIT CoreCLR de .NET
  • Si Unity achève sa modernisation .NET basée sur CoreCLR, des gains de performance majeurs seront possibles à la fois dans les jeux et dans l’éditeur, ce qui pourrait supprimer cette taxe de performance cachée pour tous les projets Unity

Pourquoi Unity utilise Mono

  • Unity utilise le framework Mono pour exécuter le code C# depuis 2006
    • À l’époque, Mono était la seule implémentation .NET multiplateforme, et son caractère open source permettait à Unity de le modifier
  • À partir de 2014, Microsoft a publié .NET Core en open source, puis lancé .NET Core 1.0 en 2016
    • Depuis, l’écosystème .NET a progressé rapidement avec le compilateur Roslyn, un nouveau JIT et de nombreuses améliorations de performance
  • En 2018, des ingénieurs d’Unity ont indiqué travailler sur un portage de CoreCLR, avec l’espoir d’un gain de performance de 2x à 10x par rapport à Mono
  • Pourtant, à la fin de l’année 2025, il reste toujours impossible d’exécuter un jeu sur une base CoreCLR

L’écart de performance entre Mono et .NET

  • Le code de simulation d’un projet Unity a été exécuté en dehors d’Unity sous .NET pour une comparaison directe
    • Environnement Unity/Mono : 100 secondes, environnement .NET : 38 secondes (en mode Debug)
    Publicité
  • En mode Release, l’écart demeure avec 30 secondes pour Mono contre 12 pour .NET
    • .NET se montre aussi très efficace en optimisation multithread, en générant par exemple une carte 4K×4K en moins de 3 secondes
  • La principale cause est la génération de code inefficace de Mono, avec un écart de vitesse de 15x même sur une boucle simple

Comparaison d’assemblage : Mono vs .NET

  • Comparaison de l’assemblage x64 généré à partir du même code de test
    • Le JIT de .NET déplace les invariants de boucle hors de la boucle (hoisting) et n’effectue qu’un minimum d’opérations sur registres
    • Mono répète des copies mémoire avec des dizaines d’instructions mov et souffre d’un inlining inefficace, ce qui pénalise les performances
  • Temps d’exécution d’une boucle répétée sur int.MaxValue
    • .NET : 750 ms, Mono : 11 500 ms, Unity Editor (Debug) : 67 000 ms
  • Mono répète à l’intérieur de la boucle des déplacements mémoire et des comparaisons inutiles

Ce que signifie l’adoption de CoreCLR

  • CoreCLR apporte des fonctionnalités modernes comme un JIT récent, l’API Span<T>, les optimisations SIMD et la prise en charge des instructions matérielles
    • Ces fonctions laissent entrevoir un gain de performance supplémentaire d’au moins 2x
    Publicité
  • Le compilateur Burst de Unity génère du code natif sur la base de LLVM, mais avec des limitations sur les fonctionnalités C#
    • Le JIT moderne de CoreCLR pourrait offrir des performances proches de Burst avec moins de contraintes sur le langage
  • CoreCLR prend aussi en charge l’AOT (compilation anticipée), ce qui peut améliorer la vitesse de démarrage et répondre aux besoins des plateformes limitant le JIT (comme iOS)
    • Unity indique toutefois vouloir continuer à maintenir IL2CPP

Conclusion : Unity doit moderniser son .NET

  • Par rapport au .NET moderne, Mono affiche des performances d’exécution 1,5x à 3x plus lentes ou davantage, ce qui constitue un coût caché pour l’ensemble des projets Unity
  • Effets attendus de l’adoption de CoreCLR
    • Amélioration des performances runtime, itérations de build plus rapides, GC amélioré, suppression du domain reload, part accrue du code managé
  • La feuille de route de Unity 6.x inclut la modernisation .NET, mais celle-ci est prévue après 2026
  • Une fois la prise en charge de CoreCLR achevée, Unity pourrait offrir une véritable révolution des performances aux développeurs comme aux joueurs
  • Pour l’instant, les limites de Mono restent un goulot d’étranglement de performance pour tout l’écosystème Unity

13 commentaires

 
dunward 2026-01-05

Je suis très heureux de voir un article lié à Unity.
Je l’ai bien lu.

 
quack337 2025-12-31

Ah… Donc Mono semble toujours reposer sur le legacy .NET Framework…
Ce n’est pas un jeu, mais je suis en train de migrer une appli financière WinForm d’environ 1000 lignes en .NET4.8 + LINQ to SQL vers .NET10 + Entity Framework, et la différence de performance se ressent clairement. Un calcul qui prenait 10 secondes est parfois tombé à 3 !

 
mhcoma 2025-12-30

S’il est adopté avec succès, on peut s’attendre à ce que l’optimisation des nombreux jeux indés s’améliore probablement...

 
intajon 2025-12-30

J’aimerais bien qu’ils ajoutent aussi la compatibilité NuGet (ou est-ce juste que je ne connais pas bien Unity ?).

 
sonohoshi 2025-12-31

Ce n’est pas pris en charge officiellement, mais il existe bien un projet open source appelé NuGetForUnity.

 
rkttu 2025-12-30

En théorie, les packages NuGet ciblant .NET Standard 2.0 sont censés pouvoir être chargés et utilisés aussi dans l’environnement Unity… mais j’ai l’impression qu’il y a quand même pas mal d’aspects peu pratiques.

https://learn.microsoft.com/ko-kr/dotnet/…

 
rkttu 2025-12-30

Je pense qu’une autre raison pour laquelle Mono doit absolument être modernisé vers CoreCLR, c’est que Unity n’aura probablement ni les moyens ni une forte volonté d’investir dans l’amélioration des performances de Mono. Je pense qu’il est temps de régler au plus vite l’héritage de l’époque .NET Framework. :-D

 
rkttu 2025-12-30

Et je pense qu’il serait bon de prendre aussi en compte le fait qu’à partir de .NET 10, le problème qu’ils voulaient résoudre auparavant avec IL2CPP est désormais traité avec une approche différente, mais pertinente (Native AOT).

Bien sûr, la limite est qu’il n’y a pas de génération de code C++ modifiable en cours de route, mais au final, la production de binaires natifs sans Just-In-Time, amorcée avec .NET 8, est devenue encore plus mature avec .NET 10.

Pour cette raison, je ne pense pas que continuer à repousser la modernisation vers CoreCLR soit un bon choix pour Unity. Ou bien un remplacement plus radical, en basculant complètement vers un autre langage ou une autre base, pourrait s’avérer plus pertinent !

 
sonohoshi 2025-12-30

Unity n’a probablement ni les moyens ni une réelle volonté d’investir dans l’amélioration des performances de Mono.

Je suis moi aussi tout à fait d’accord sur ce point...

 
sonohoshi 2025-12-30

C’est vrai, mais je ne vois pas vraiment pourquoi comparer spécifiquement les performances de l’éditeur... À la rigueur, ils auraient au moins pu faire la comparaison avec une build debug ? Ou non, peut-être que ça aurait été encore moins convaincant ? Cela dit, d’un autre côté, IL2CPP comme Mono me semblent tous deux être des technologies dépassées.

 
foriequal0 2025-12-30

Sur les gros projets, les performances de l’éditeur ont aussi leur importance, car elles dégradent fortement l’expérience de développement. L’ouverture de l’éditeur est lente, l’import des assets aussi, et la boucle de débogage/test est lente elle aussi...

 
sonohoshi 2025-12-30

Ah... bien sûr, c’est aussi important. Quand je l’ai lu pour la première fois, j’ai eu l’impression que l’auteur voulait surtout parler d’un problème plus fondamental de vitesse d’exécution du code. C’est vrai aussi que, comme vous le dites, Unity a un éditeur lent, des imports lents, et une boucle de test globalement lente...

 
GN⁺ 2025-12-30
Réactions sur Hacker News
  • En tant que développeur de jeux professionnel, je ne participe généralement pas aux discussions sur Unity, mais ce sujet touche directement à mon domaine d’expertise, donc je laisse un avis
    Certains passages donnaient l’impression d’avoir été écrits par quelqu’un ayant peu d’expérience pratique avec Unity
    1. La séparation entre simulation et couche de présentation est depuis longtemps une méthode classique d’optimisation des performances (autrefois surtout implémentée en C++)
    2. La plupart des jeux sont publiés avec IL2CPP, pas avec le backend Mono
    3. Dans les versions récentes de Unity, il est courant d’utiliser Burst Compiler et HPC# pour les performances. Le Job System aide beaucoup
    4. Profiler dans l’éditeur n’a quasiment aucun intérêt, parce qu’il est bien plus lent qu’un build de debug
      En résumé, si les développeurs Unity attendent cette mise à jour, c’est moins pour les performances que pour l’accès à des fonctionnalités de langage modernes. Et il est courant de minimiser le GC pendant l’exécution, ou de le contourner avec de la mémoire non managée et DOTS
    • On dirait que tu t’es un peu trop laissé porter par le marketing de Unity. Unity a toujours avancé en améliorant par petites touches des systèmes problématiques
      IL2CPP n’est qu’un générateur de code de faible qualité qui convertit le .NET IL en C++, puis s’en remet à un compilateur optimisant
      On peut le voir dans cet article du blog Unity sur les entrailles d’IL2CPP
      Burst/HPC# suit aussi des tendances comme ECS ou SoA, mais les performances restent inférieures à du C++ bien écrit ou à du C# CoreCLR
      En plus, ces technologies sont fermées et spécifiques à Unity, donc inutilisables ailleurs. Unity fait toujours son marketing avec des benchmarks comparés au Mono lent
      Au final, Unity n’aura pas d’autre choix que d’adopter CoreCLR, et à ce moment-là ils découvriront que du C# ordinaire est plus rapide que tout leur code compliqué existant
    • Merci pour ton retour, en tant qu’auteur de l’article. Notre jeu a une architecture totalement indépendante des types Unity et des DLL Unity, donc il diffère d’un jeu Unity classique
      Nous n’utilisons pas IL2CPP parce qu’il n’est pas compatible avec des choses comme le chargement dynamique de DLL à l’exécution, la réflexion, ou le packing de structures avec FieldOffset
      Les moddeurs peuvent étendre les fonctionnalités via de l’injection IL, ce qui accélère au final le développement
      Nous n’aimons pas Burst et HPC# à cause de leur complexité et de leurs contraintes. L’écart de performance entre Mono et .NET est d’autant plus frustrant
      Le profiling dans l’éditeur nous a aussi été utile, car il montrait des gains de performance dans des proportions proches de celles du build réel. En revanche, le profiler intégré de Unity est imprécis, donc nous utilisons notre propre système de traçage
      Le GC reste un problème. Le traitement des chaînes et l’UI génèrent des déchets mémoire à chaque frame. Avec CoreCLR, nous pourrions bénéficier de meilleures API et d’un GC compactant pour réduire la fragmentation mémoire
  • Unity est facile d’accès, mais sa structure monolithique entraîne une dette technique énorme
    L’Asset Store est excellent, mais le moteur lui-même donne une impression de manque de finition
    Le scripting basé sur Mono est structurellement complexe à faire migrer vers CoreCLR
    Si Unity veut vraiment améliorer son cœur, il faut redesigner tout l’éditeur comme Blender 3.x
    Aujourd’hui, on a l’impression d’une UI de 1999
    • Le plus gros problème de Unity, c’est qu’ils abandonnent les nouvelles fonctionnalités avant de les finir
      D’innombrables plugins et outils restent bloqués au stade “0.x-preview”, puis 5 à 10 ans plus tard ne fonctionnent plus ou sont enterrés sous de nouveaux assets
      C’est pour ça que je n’utilise désormais que des versions 1.0 ou supérieures. Sinon on finit dépendant de plugins abandonnés qu’il faut de toute façon reporter plus tard
      Tout le monde y perd : Unity, les développeurs et les utilisateurs
    • Unity donne l’impression d’une entreprise qui a perdu sa direction
      En interne, ses échecs dans le développement de jeux maison lui ont fait perdre le sens concret de la production de jeux
      Ils se contentent d’ajouter les fonctionnalités demandées, sans vision cohérente
    • WebGPU est une couche d’abstraction comme Vulkan
      Si la performance est prioritaire, mieux vaut appeler Vulkan directement, et si c’est la portabilité qui compte, alors il faut utiliser WebGPU
      Chaque navigateur l’implémente différemment, ce qui crée du surcoût, mais cela pourrait être résolu si WebGPU était fourni au niveau du pilote de l’OS
    • Avec Unity, on a toujours l’impression qu’il faut trouver une solution de contournement pour faire quoi que ce soit
      À l’inverse, Godot fournit des briques simples et pertinentes qui permettent de construire librement ce qu’on veut
    • Unity casse souvent les API de ses propres fonctionnalités, au point que même les tutoriels ne fonctionnent plus
      L’Asset Store a le même problème : les soucis de compatibilité entre versions rendent la maintenance difficile, et la plupart des assets finissent à l’abandon
      Quand Unity rachète un asset utile, ils l’intègrent mal, et les assets concurrents disparaissent
      À l’inverse, Unreal Engine fournit ces fonctionnalités comme des éléments intégrés au moteur
  • Le GC de Unity utilise Boehm GC, ce qui le rend bien plus lent que .NET ou Mono
    Unity n’a pas non plus de plan pour introduire un meilleur GC dans IL2CPP
    Avec un éditeur basé sur CoreCLR, l’éditeur pourrait même devenir plus rapide qu’un build
    Discussion liée : Unity CoreCLR et modernisation de .NET
    • Améliorer le GC, c’est bien, mais dans un jeu, le plus important reste de maintenir les allocations à 0 par frame
      Si le GC incrémental fonctionne bien, les problèmes de saccades ne sont pas si graves
    • Lors du passage à CoreCLR, la suppression du domain reload accélérera fortement l’éditeur
    • Mono dispose déjà d’un GC précis, donc on peut se demander pourquoi Unity utilise Boehm
  • Depuis que Unity a commencé à parler d’un portage vers CoreCLR en 2018, le chemin est long
    Comme le C# est devenu très rapide en soi, Unity doit absolument concentrer tous ses efforts sur cette transition
    Dans notre équipe, passer de .NET Framework 4.7.2 à .NET 6 a pris quelques mois, puis les mises à niveau LTS suivantes ont été de l’ordre de quelques heures
  • Unity ne semble plus avoir les capacités techniques pour mener à bien une migration vers CoreCLR
    Tout est sans cesse repoussé, et les responsables s’en vont
    En alternative, je recommande le moteur Stride basé sur .NET 10, qui n’a pas de surcoût de frontière comme Unity
    • Les progrès sont lents, mais Unity est effectivement en train de pousser la transition vers CoreCLR
    • Stride ne fournit qu’une partie des fonctionnalités de Unity
      Godot est open source, mais son support C# est instable, et si les builds Web ne marchent pas, il est inadapté aux game jams
      Il faut une vraie solution sandbox avec support GPU
    • Ce n’est pas un problème de niveau technique, mais de ressources et de leadership
      Les priorités changent constamment, les exigences sont révisées, et le travail doit être refait
      Les développeurs restent excellents, mais il manque une impulsion cohérente
    • Unity supporte aussi la charge de devoir abandonner ou intégrer les nombreuses technologies et add-ons qu’ils ont acquis
      Pour un CEO, ce type de réécriture à grande échelle est aussi une décision à haut risque
  • J’ai développé le système Circuits de Rec Room, avec l’expérience de retirer la dépendance à Unity pour pouvoir tester sur CoreCLR
    Résultat : les performances se sont nettement améliorées, et la réduction de la dépendance au moteur a aussi facilité la maintenance du code
    Le fait de n’exposer les concepts Unity qu’aux endroits nécessaires, puis d’imposer des frontières via les tests, m’a fait ressentir concrètement la valeur de la séparation logique
  • Je me demande pourquoi l’auteur utilise Mono plutôt qu’IL2CPP. Comme IL2CPP est bien plus rapide, les builds Mono sont dépassés. Si la performance compte, IL2CPP devrait être la première étape
  • Mon rêve, c’est de pouvoir installer directement le SDK .NET sur un téléphone Android et l’exécuter en natif
    Avec un accès root, et combiné à des outils réseau comme WireGuard ou Tailscale, ce serait aussi parfait comme serveur portable
    Avec le nouveau GC de .NET 10, les saccades en jeu pourraient quasiment disparaître
    Pour l’instant, je streame les jeux de mon PC principal vers mon téléphone avec Sunlight + Moonlight
    Grâce à l’écran OLED à haut taux de rafraîchissement, la batterie se vide aussi assez peu
    • Les limitations sur le root existent pour des raisons de sécurité. Le but est de rendre le root difficile afin d’éviter les infections via les ports USB dans les lieux publics
    • On peut faire quelque chose de similaire avec MAUI(Xamarin)
      Ce n’est pas le SDK .NET, mais comme le runtime Mono est embarqué dans l’application, la sensation est proche
  • Je me demande pourquoi Unity n’est toujours pas passé complètement à .NET
    Comme les avantages cross-platform de Mono ont déjà disparu, je ne comprends pas pourquoi ils continuent à maintenir un hack complexe comme IL2CPP
    • Il y a un énorme piège des coûts irrécupérables. Ils ont déjà investi massivement dans Mono, IL2CPP, Burst, etc., donc il est difficile de changer de direction
    • Unity a très fortement personnalisé Mono, donc ce n’est pas remplaçable simplement
      Des années de modifications non standard se sont accumulées, au point qu’à part une grande entreprise il serait difficile d’optimiser de nouveau tout cela
  • Je suis surpris que Unity ait commencé sa transition vers CoreCLR dès 2018 sans encore l’avoir terminée
    Pour un projet de cette ampleur, j’aurais pensé que 1 à 2 ans auraient suffi