- El runtime Mono que usa Unity muestra una velocidad de ejecución notablemente inferior frente a .NET moderno, con casos donde el mismo código en C# presenta una diferencia de hasta 15 veces
- En código real de juegos, la ejecución de Unity basada en Mono tardó 100 segundos, mientras que el mismo código en .NET tardó 38 segundos, lo que también afecta mucho la eficiencia de depuración y pruebas
- Incluso en modo Release, Mono tardó 30 segundos y .NET 12 segundos, por lo que la diferencia de rendimiento de más de 2.5 veces se mantiene aun en entornos optimizados
- La causa está en la compilación JIT ineficiente de Mono y sus fallas de inlining, además de copias de memoria excesivas, en contraste con las optimizaciones modernas del JIT CoreCLR de .NET
- Si Unity completa su modernización de .NET basada en CoreCLR, podría lograr una gran mejora de rendimiento tanto en los juegos como en el editor, eliminando así un impuesto oculto de rendimiento en todos los proyectos de Unity
Antecedentes del uso de Mono en Unity
- Unity usa el framework Mono para ejecutar código en C# desde 2006
- En ese momento, Mono era la única implementación multiplataforma de .NET y, al ser de código abierto, Unity podía modificarlo
- Desde 2014, Microsoft publicó .NET Core como código abierto y en 2016 lanzó .NET Core 1.0
- Desde entonces, el ecosistema .NET ha avanzado rápidamente con el compilador Roslyn, un nuevo JIT y múltiples mejoras de rendimiento
- En 2018, ingenieros de Unity dijeron que estaban trabajando en el port de CoreCLR y esperaban una mejora de rendimiento de 2 a 10 veces frente a Mono
- Sin embargo, hasta finales de 2025 sigue siendo imposible ejecutar juegos sobre CoreCLR
Brecha de rendimiento entre Mono y .NET
- Se comparó directamente ejecutando en .NET, fuera de Unity, el código de simulación de un proyecto de Unity
- Entorno Unity/Mono: 100 segundos, entorno .NET: 38 segundos (en modo Debug)
- En modo Release, la diferencia se mantiene: Mono 30 segundos y .NET 12 segundos
- .NET destaca por su optimización multihilo, por ejemplo al generar un mapa de 4K×4K en menos de 3 segundos
- La causa principal es la generación ineficiente de código en Mono, con diferencias de 15 veces incluso en bucles simples
Comparación de ensamblado: Mono vs .NET
- Comparando el ensamblado x64 generado a partir del mismo código de prueba
- El JIT de .NET mueve invariantes del bucle fuera del bucle (hoisting) y realiza solo la mínima cantidad de operaciones en registros
- Mono repite copias de memoria con decenas de instrucciones
mov y su inlining ineficiente degrada el rendimiento
- Tiempo de ejecución del bucle repetido con
int.MaxValue
- .NET: 750ms, Mono: 11,500ms, Unity Editor (Debug): 67,000ms
- Mono repite movimientos de memoria y comparaciones innecesarias dentro del bucle
Qué significa adoptar CoreCLR
- CoreCLR ofrece funciones modernas como JIT actualizado, API
Span<T>, optimizaciones SIMD y soporte para instrucciones de hardware
- Estas capacidades podrían aportar una mejora adicional de más de 2 veces en rendimiento
- El compilador Burst de Unity genera código nativo basado en LLVM, pero tiene limitaciones en las funciones de C#
- El JIT moderno de CoreCLR puede ofrecer un rendimiento similar al de Burst con menos restricciones del lenguaje
- CoreCLR también soporta AOT (compilación anticipada), lo que puede mejorar la velocidad de arranque y permitir compatibilidad con plataformas que limitan JIT, como iOS
- Aun así, Unity mantiene su postura de seguir conservando IL2CPP
Conclusión: Unity necesita modernizar .NET
- Mono muestra un rendimiento de ejecución entre 1.5 y 3 veces o más lento que el .NET moderno, lo que funciona como un costo oculto en todos los proyectos de Unity
- Efectos esperados al adoptar CoreCLR
- Mejor rendimiento en runtime, iteraciones de compilación más rápidas, mejoras en GC, eliminación del domain reload, mayor proporción de código administrado
- La hoja de ruta de Unity 6.x incluye .NET Modernization, pero está prevista para después de 2026
- Cuando el soporte de CoreCLR esté completo, podría aportar una verdadera transformación de rendimiento tanto para desarrolladores de Unity como para jugadores
- Por ahora, las limitaciones de Mono siguen siendo un cuello de botella de rendimiento para todo el ecosistema de Unity
13 comentarios
Ah... parece que Mono sigue basado en el legacy .NET Framework...
No es un juego, pero estoy migrando una app financiera de WinForm de unas 100 mil líneas, con .NET 4.8 + LINQ to SQL, a .NET 10 + Entity Framework, y se siente claramente mucho más rápida. ¡Incluso hay tareas de cálculo que pasaron de tardar 10 segundos a 3!
Ojalá también agregaran compatibilidad con NuGet (¿será porque no conozco bien Unity?)
No tiene soporte oficial, pero existe un proyecto de código abierto llamado NuGetForUnity.
En teoría, los paquetes de NuGet dirigidos a .NET Standard 2.0 deberían poder cargarse y usarse también en el entorno de Unity... pero parece que, de todos modos, hay bastantes puntos incómodos.
https://learn.microsoft.com/ko-kr/dotnet/…
Es cierto, pero no entiendo muy bien por qué comparan específicamente el rendimiento del editor... Aunque sea, ¿no podrían haber traído una comparación con una build de depuración? ¿O no? ¿Entonces eso habría sido todavía menos convincente? Por otro lado, da la impresión de que tanto IL2CPP como Mono son tecnologías igual de anticuadas.
En proyectos grandes, el rendimiento del editor también importa porque puede deteriorar mucho la experiencia de desarrollo. La apertura del editor es lenta, la importación de assets también es lenta, y el ciclo de depuración/pruebas también es lento...
Ah... sí, claro que esto también es importante. Cuando lo leí por primera vez, me pareció que el autor quería hablar de un problema más fundamental relacionado con la velocidad de ejecución del código. También es cierto que, como dices, Unity tiene un editor lento, importaciones lentas y, en general, un ciclo de pruebas lento...
Qué gusto ver una publicación relacionada con Unity.
La leí con mucho interés.
Si se implementa con éxito, probablemente mejorará la optimización de muchísimos juegos indie...
Otra razón por la que Mono definitivamente debe modernizarse hacia CoreCLR, creo, es que Unity probablemente no tiene ni las condiciones ni la voluntad de invertir mucho en mejorar el rendimiento de Mono. Creo que lo correcto es dejar atrás cuanto antes el legado de la era de .NET Framework. :-D
Y también me parece que sería bueno considerar que, a partir de .NET 10, el problema que antes intentaban resolver con IL2CPP está siendo abordado con precisión, aunque en una dirección distinta de desarrollo, mediante Native AOT.
Claro, la limitación es que en el proceso no se genera código C++ editable en el medio, pero al final la producción de binarios nativos sin que ocurra Just-In-Time ha ido madurando desde .NET 8 y llegó aún más consolidada en .NET 10.
Por esa razón, creo que seguir postergando la modernización hacia CoreCLR no va a ser una buena decisión para Unity. O quizá reemplazarlo por completo y pasarse a otro lenguaje o a otra base podría ser una alternativa más válida.
> Es probable que Unity no tenga ni las condiciones ni mucha voluntad de invertir en mejorar el rendimiento de Mono.
También estoy totalmente de acuerdo con esto...
Comentarios de Hacker News
Hubo varias partes del texto que parecían escritas por alguien con poca experiencia desarrollando en Unity.
En resumen, la razón por la que los desarrolladores de Unity esperan esta actualización no es tanto por el rendimiento sino por el acceso a funciones modernas del lenguaje. Y también es común minimizar el GC durante la ejecución o evitarlo usando memoria no administrada y DOTS.
IL2CPP no es más que un generador de código de baja calidad que convierte .NET IL a C++, y depende de compiladores optimizadores.
Se puede ver en la explicación interna de IL2CPP en el blog de Unity.
Burst/HPC# también sigue tendencias como ECS o SoA, pero rinde peor que C++ bien escrito o que C# sobre CoreCLR.
Además, estas tecnologías son cerradas y exclusivas de Unity, así que no se pueden usar fuera de ahí. Unity siempre hace marketing con benchmarks comparándolas con Mono lento.
Al final Unity no tendrá más remedio que adoptar CoreCLR, y cuando eso pase se darán cuenta de la realidad: que el C# normal puede ser más rápido que todo ese código complejo que ya tienen.
No usamos IL2CPP porque no es compatible con carga dinámica de DLL en tiempo de ejecución, reflection ni empaquetado de structs con FieldOffset.
Los modders pueden ampliar funcionalidades con inyección de IL, y eso al final acelera el desarrollo.
No me gustan Burst ni HPC# porque traen mucha complejidad y restricciones. La diferencia de rendimiento entre Mono y .NET lo hace todavía más frustrante.
El perfilado en el editor también fue útil porque mostraba mejoras de rendimiento en proporciones similares al build real. En cambio, el profiler por defecto de Unity era impreciso, así que usamos un sistema de trazas hecho por nosotros.
El GC sigue siendo un problema. El manejo de strings y la UI generan basura en cada frame. Con CoreCLR tendríamos APIs mejores y un GC con movimiento, lo que ayudaría a reducir la fragmentación de memoria.
El Asset Store es excelente, pero el motor en sí se siente poco pulido.
La base de scripting sobre Mono es estructuralmente compleja de migrar a CoreCLR.
Si Unity de verdad quiere mejorar su núcleo, tendría que rediseñar todo el editor, como hizo Blender 3.x.
Ahora mismo se siente como una UI de 1999.
Montones de plugins y herramientas se quedan en fase “0.x-preview”, y 5 o 10 años después ya no funcionan o quedan enterrados bajo assets nuevos.
Por eso ahora solo uso cosas con versión 1.0 o superior. Si no, terminas dependiendo de plugins abandonados y tarde o temprano tienes que portear todo otra vez.
Todos pierden: Unity, los desarrolladores y los usuarios.
Internamente le falta sensibilidad real para hacer juegos por sus fracasos en desarrollo de juegos propios.
Solo agregan funciones que les piden, pero no tienen una visión coherente.
Si el rendimiento importa, es mejor llamar Vulkan directamente, y si importa la portabilidad, entonces hay que usar WebGPU.
Como la implementación cambia entre navegadores, aparece sobrecarga, pero eso podría resolverse si el sistema operativo ofreciera WebGPU a nivel de driver.
En cambio, Godot te da bloques de construcción simples y con sentido para crear libremente lo que quieras.
En el Asset Store pasa lo mismo: por problemas de compatibilidad entre versiones, mantener cosas se vuelve difícil y la mayoría termina siendo assets abandonados.
Cuando Unity compra assets útiles, ni siquiera los integra bien, y los competidores desaparecen.
En cambio, Unreal Engine ofrece estas funciones como parte integrada del motor.
Unity tampoco parece tener planes de meter un GC mejor en IL2CPP.
Cuando salga un editor basado en CoreCLR, incluso el editor podría ser más rápido que el build.
Debate relacionado: CoreCLR y modernización de .NET en Unity
Si el GC incremental funciona bien, el stutter tampoco debería ser un gran problema.
Como C# se volvió muy rápido por sí mismo, Unity tiene que poner todos sus recursos en esta transición.
A nuestro equipo le tomó unos meses pasar de .NET Framework 4.7.2 a .NET 6, y después las actualizaciones entre versiones LTS fueron cuestión de horas.
Todo sigue retrasándose y los líderes se van.
Como alternativa recomiendo Stride, basado en .NET 10, que no tiene el mismo overhead de frontera que Unity.
Godot es open source, pero su soporte para C# es inestable, y si no puedes hacer builds web, no sirve bien para game jams.
Hace falta una solución sandbox real con soporte de GPU.
Las prioridades cambian todo el tiempo y los requisitos se modifican, así que el trabajo se repite.
Los desarrolladores siguen siendo muy buenos, pero falta impulso constante.
Un rewrite de esta magnitud también es, desde la perspectiva de un CEO, una decisión muy riesgosa.
El resultado fue una mejora importante de rendimiento, y al reducir la dependencia del motor también se volvió más fácil mantener el código.
Al exponer conceptos de Unity solo donde hacía falta y reforzar los límites con tests, confirmé el valor de una separación lógica.
Con acceso root y combinado con herramientas de red como WireGuard o Tailscale, sería perfecto también como servidor portátil.
Con el nuevo GC de .NET 10, el stutter en juegos casi desaparecería.
Por ahora juego en el teléfono haciendo streaming desde mi PC principal con Sunlight + Moonlight.
Gracias a la pantalla OLED de alta tasa de refresco, el consumo de batería también es bajo.
No es el SDK de .NET, pero como el runtime de Mono va incluido en la app, en la práctica se siente parecido.
Si las ventajas multiplataforma de Mono ya desaparecieron, no se entiende por qué siguen manteniendo hacks complejos como IL2CPP.
Se fueron acumulando años de modificaciones no estándar, y salvo que seas una empresa enorme, no es fácil volver a optimizar todo desde cero.
Uno pensaría que un proyecto así debería tomar solo 1 o 2 años.