- O runtime Mono usado pelo Unity apresenta velocidade de execução significativamente mais lenta em comparação com o .NET moderno, com casos em que o mesmo código C# pode ter diferença de até 15x
- Em código real de jogo, a execução no Unity baseado em Mono levou 100 segundos, enquanto a mesma execução em .NET levou 38 segundos, afetando fortemente a eficiência de depuração e testes
- Mesmo no modo Release, o Mono levou 30 segundos e o .NET 12 segundos, mantendo uma diferença de desempenho de mais de 2,5x mesmo em ambiente otimizado
- A causa está na compilação JIT ineficiente e falhas de inlining do Mono, além do excesso de cópias de memória, em contraste com as otimizações modernas do JIT CoreCLR do .NET
- Quando o Unity concluir a modernização do .NET baseada em CoreCLR, será possível obter grandes ganhos de desempenho tanto em jogos quanto no editor, o que deve eliminar o imposto oculto de desempenho em todos os projetos Unity
Contexto do uso do Mono no Unity
- O Unity usa o framework Mono para executar código C# desde 2006
- Na época, o Mono era a única implementação multiplataforma de .NET, além de ser open source e permitir modificações pelo Unity
- A partir de 2014, a Microsoft liberou o .NET Core como open source e, em 2016, lançou o .NET Core 1.0
- Desde então, o ecossistema .NET evoluiu rapidamente com o compilador Roslyn, novo JIT e melhorias de desempenho
- Em 2018, engenheiros do Unity afirmaram que estavam trabalhando no port de CoreCLR, esperando um ganho de desempenho de 2x a 10x em relação ao Mono
- Porém, até o fim de 2025, ainda não é possível executar jogos com base em CoreCLR
Diferença de desempenho entre Mono e .NET
- O código de simulação de um projeto Unity foi executado em .NET fora do Unity para comparação direta
- Ambiente Unity/Mono: 100 segundos, ambiente .NET: 38 segundos (com base no modo Debug)
- No modo Release, a diferença permanece: Mono em 30 segundos, .NET em 12 segundos
- O .NET também se destaca pela otimização multithread, gerando um mapa 4K×4K em menos de 3 segundos
- A geração ineficiente de código do Mono é a principal causa, com diferenças de velocidade de 15x até mesmo em loops simples
Comparação de assembly: Mono vs .NET
- Comparando o assembly x64 gerado a partir do mesmo código de teste
- O JIT do .NET move invariantes de loop para fora do loop (hoisting) e realiza apenas o mínimo necessário de operações em registradores
- O Mono repete cópias de memória com dezenas de instruções
mov e perde desempenho com inlining ineficiente
- Tempo de execução de um loop com repetição de
int.MaxValue
- .NET: 750ms, Mono: 11.500ms, Unity Editor(Debug): 67.000ms
- O Mono repete desnecessariamente movimentações de memória e operações de comparação dentro do loop
O significado da adoção do CoreCLR
- O CoreCLR oferece recursos modernos como JIT de última geração, API
Span<T>, otimização SIMD e suporte a instruções de hardware
- Esses recursos podem possibilitar ganhos adicionais de desempenho acima de 2x
- O compilador Burst do Unity gera código nativo com base em LLVM, mas tem limitações em recursos da linguagem C#
- O JIT moderno do CoreCLR pode oferecer desempenho semelhante ao Burst com menos restrições de linguagem
- O CoreCLR também oferece suporte a AOT (compilação antecipada), permitindo melhor tempo de inicialização e suporte a plataformas com restrições de JIT (como iOS)
- Ainda assim, o Unity afirma que continuará mantendo o IL2CPP
Conclusão: a necessidade de modernizar o .NET no Unity
- O Mono apresenta desempenho de execução 1,5x a mais de 3x mais lento do que o .NET moderno, funcionando como um custo oculto em todos os projetos Unity
- Efeitos esperados com a adoção do CoreCLR
- Melhor desempenho em runtime, builds iterativos mais rápidos, melhorias no GC, eliminação do domain reload, maior proporção de código gerenciado
- O roadmap do Unity 6.x inclui .NET Modernization, mas ela está prevista apenas para depois de 2026
- Quando o suporte ao CoreCLR estiver concluído, ele poderá oferecer uma revolução prática de desempenho tanto para desenvolvedores quanto para jogadores do Unity
- No momento, as limitações do Mono continuam sendo um gargalo de desempenho em todo o ecossistema Unity
13 comentários
Ah... então parece que o Mono ainda é baseado no legado .NET Framework...
Não é jogo, mas estou no meio da migração de um app financeiro em WinForm com umas 100 mil linhas, usando .NET 4.8 + LINQ to SQL, para .NET 10 + Entity Framework, e dá para sentir que ficou muito mais rápido. Teve até tarefa de cálculo que levava 10 segundos e caiu para 3!
Seria bom se também adicionassem compatibilidade com NuGet (ou será que estou dizendo isso por não conhecer bem Unity?)
Não é suporte oficial, mas existe um projeto open source chamado NuGetForUnity.
Em teoria, pacotes NuGet voltados para .NET Standard 2.0 podem ser importados e usados também no ambiente Unity... mas, pelo visto, ainda há vários pontos inconvenientes.
https://learn.microsoft.com/ko-kr/dotnet/…
É verdade, mas sinceramente não entendo por que fizeram questão de comparar o desempenho no editor... No mínimo, poderiam ter trazido uma comparação com uma build de debug, não? Ou não, será que aí ficaria ainda menos convincente? Por outro lado, tanto IL2CPP quanto Mono parecem tecnologias igualmente ultrapassadas.
Em projetos grandes, o desempenho do editor também importa, porque prejudica bastante a experiência de desenvolvimento. O editor demora para abrir, a importação de assets também é lenta, e o ciclo de depuração/testes também é lento...
Ah... claro, isso também é importante. Quando li pela primeira vez, pareceu que o autor queria discutir algo mais fundamental sobre a velocidade de execução do código. Também é verdade que, como você disse, o Unity tem um editor lento, importação lenta e, no geral, um ciclo de testes lento...
É muito bom ver um artigo relacionado à Unity.
Li com bastante interesse.
Se isso for implementado com sucesso, é provável que a otimização dos inúmeros jogos indie melhore bastante...
Acho que outro motivo pelo qual o Mono precisa necessariamente ser modernizado para o CoreCLR é que a Unity provavelmente não tem nem condições nem muita disposição para investir em melhorias de desempenho do Mono. Também acho que está mais do que na hora de encerrar de vez os legados da era do .NET Framework. :-D
E também acho que seria bom considerar que, a partir do .NET 10, o problema que eles queriam resolver no passado com o IL2CPP está sendo tratado de forma precisa, embora em outra direção de desenvolvimento, também com o
Native AOT.Claro, a limitação aqui é que não se gera código C++ editável no meio do processo, mas, no fim das contas, a produção de binários nativos sem ocorrência de Just-In-Time ficou mais madura desde o .NET 8 e chegou ainda mais amadurecida no .NET 10.
Por esse motivo, acho que continuar adiando a modernização para o CoreCLR não será uma boa escolha para a Unity. Ou talvez uma substituição completa, migrando para outra linguagem ou outra base, possa até ser mais válida!
> É improvável que a Unity tenha condições ou vontade de investir em melhorias de desempenho do Mono
Também concordo fortemente com isso...
Comentários do Hacker News
Vi algumas partes no texto que pareciam ter sido escritas por alguém com pouca experiência prática em desenvolvimento com Unity
Resumindo: o motivo de os desenvolvedores Unity estarem animados com esta atualização não é tanto ganho de desempenho, e sim acesso a recursos modernos da linguagem. E também é comum minimizar o GC em runtime ou contornar isso com memória não gerenciada e DOTS
IL2CPP não passa de um gerador de código de baixa qualidade que converte .NET IL em C++, dependente de compiladores otimizadores
Dá para ver isso em IL2CPP Internals no blog da Unity
Burst/HPC# também seguem tendências como ECS e SoA, mas o desempenho fica abaixo de um C++ bem escrito ou de C# com CoreCLR
Além disso, essas tecnologias são fechadas e exclusivas da Unity, então não podem ser usadas fora dela. A Unity sempre faz marketing com benchmarks comparando com o Mono lento
No fim, a Unity também foi forçada a aceitar o CoreCLR, e quando isso acontecer vai perceber a realidade de que código C# comum é mais rápido que o código complexo que ela empilhou até aqui
Não usamos IL2CPP porque ele não é compatível com carregamento de DLL em runtime, reflexão, empacotamento de structs com FieldOffset e coisas do tipo
Modders podem expandir funcionalidades com injeção de IL, o que no fim acelera o desenvolvimento
Não gosto de Burst e HPC# por causa da complexidade e das limitações. A diferença de desempenho entre Mono e .NET torna isso ainda mais frustrante
O profiling no editor também foi útil porque mostrava melhorias de desempenho em proporções parecidas com as do build real. Em vez do profiler padrão da Unity, que é impreciso, usamos um sistema de rastreamento feito por nós
O GC continua sendo um problema. Processamento de strings e UI geram lixo a cada frame. Com CoreCLR, deve ser possível ter APIs melhores e GC móvel, reduzindo problemas de fragmentação de memória
A Asset Store é excelente, mas o motor em si passa uma sensação de falta de acabamento.
O scripting baseado em Mono é estruturalmente complexo demais para migrar para CoreCLR com facilidade
Se a Unity quiser realmente melhorar o Core, vai precisar redesenhar o editor inteiro, como o Blender 3.x.
Hoje ele ainda parece uma UI de 1999
Incontáveis plugins e ferramentas param na fase “0.x-preview” e, 5 a 10 anos depois, já não funcionam ou ficam soterrados por assets novos
Por isso agora eu só uso versões 1.0 ou superiores. Caso contrário, você acaba dependente de plugin abandonado e depois precisa portar tudo de novo
Isso é ruim para a Unity, para os desenvolvedores e para os usuários
Internamente, a falha no desenvolvimento dos próprios jogos fez com que faltasse noção real de produção de jogos
Ela apenas adiciona os recursos que pedem, sem uma visão consistente
Se desempenho for o mais importante, é melhor chamar Vulkan diretamente; se portabilidade for mais importante, use WebGPU
Como a implementação varia entre navegadores, isso gera overhead, mas isso poderia ser resolvido se o WebGPU fosse oferecido no nível de driver do sistema operacional
Já o Godot oferece blocos de construção simples e significativos, que permitem montar o que você quiser com liberdade
O mesmo vale para a Asset Store: problemas de compatibilidade entre versões tornam a manutenção difícil, e a maioria vira asset abandonado
Quando a Unity compra um asset útil, ela nem sempre o integra direito, e os concorrentes desaparecem
Já a Unreal Engine oferece essas coisas em nível nativo do motor
A Unity também não parece ter planos de adotar um GC melhor no IL2CPP
Quando sair um editor baseado em CoreCLR, o editor pode até ficar mais rápido que o build
Discussão relacionada: Unity CoreCLR e modernização do .NET
Se o GC incremental funcionar bem, o problema de stutter também não fica tão grave
Como C# ficou muito mais rápido por padrão, a Unity precisa concentrar todos os esforços nessa transição
Nossa equipe levou alguns meses para sair do .NET Framework 4.7.2 e ir para o .NET 6, e depois os upgrades de versões LTS ficaram na casa de horas
Continua atrasando, e os líderes vão saindo
Como alternativa, recomendo o motor Stride baseado em .NET 10. Como no Unity, não há overhead de fronteira
O Godot é open source, mas o suporte a C# é instável e, se não houver build para Web, ele fica inadequado para game jams
Precisamos de uma solução sandbox de verdade com suporte a GPU
As prioridades mudam o tempo todo, os requisitos são alterados, e o trabalho acaba sendo refeito
Os desenvolvedores ainda são excelentes, mas falta consistência na execução
Um rewrite dessa escala é uma decisão de alto risco até do ponto de vista do CEO
Como resultado, o desempenho melhorou bastante e a manutenção do código também ficou mais fácil, graças à redução da dependência do motor
Ao expor conceitos da Unity só onde era necessário e usar testes para impor fronteiras, percebi o valor dessa separação lógica
Com acesso root e combinado com ferramentas de rede como WireGuard ou Tailscale, seria perfeito até como servidor portátil
Com o novo GC do .NET 10, o stutter em jogos também praticamente desapareceria
Hoje eu faço streaming de jogos do PC principal com Sunlight + Moonlight e jogo no celular.
Graças à tela OLED de alta taxa de atualização, o consumo de bateria também é baixo
Não é o .NET SDK, mas o runtime do Mono vem embutido no app, então na prática a sensação é parecida
A vantagem multiplataforma do Mono já desapareceu, então não entendo por que continuam mantendo hacks complexos como o IL2CPP
Anos de modificações fora do padrão foram se acumulando, e, a menos que seja uma big tech, também não é fácil otimizar tudo de novo
Para um projeto desse porte, eu imaginaria que 1 a 2 anos seriam suficientes