29 comments

[ 2.3 ms ] story [ 58.8 ms ] thread
Yeah I think Unity just doesn't have the technical skillset anymore to make the migration to coreclr. It keeps getting delayed and their tech leads keep dropping out.

Might I suggest https://github.com/stride3d/stride, which is already on .net 10 and doesn't have any cross-boundary overhead like Unity.

That's interesting. I made measurements with Mono and CoreCLR some years ago, but only with a single thread, and I came to the conclusion that their performance was essentially the same (see https://rochus.hashnode.dev/is-the-mono-clr-really-slower-th...). Can someone explain what benchmarks were actually used? Was it just the "Simple benchmark code" in listing 1?
I think the “some years ago” is pretty relevant.

.NET has heavily invested in performance. If I understand your article correctly, you tested .NET 5 which will be much slower at this point than .NET 10 is.

I also think it matters what you mean by “Mono”. Mono, the original stand-alone project has not seen meaningful updates in many years. Mono is also one of the two runtimes in the currently shipping .NET though and I suspect this runtime has received a lot of love that may not have flowed back to the original Mono project.

Will the move to CoreCLR give any speed ups in practice if the release build is complied with IL2CPP anyway? On all the games that I've worked on, IL2CPP is one of the first things that we've enabled, and the performance difference between the editor and release version is very noticeable.
The article doesn't cover it but the GC being used by Unity also performs very poorly vs. .NET, and even vs. standalone Mono, because it is using the Boehm GC. Last I heard Unity has no plans to switch IL2CPP to a better GC [1].

It'll be interesting to see how the CoreCLR editor performs. With that big of a speed difference the it might be possible for games to run better in the editor than a standalone Mono/IL2CPP build.

[1] https://discussions.unity.com/t/coreclr-and-net-modernizatio...

I recently started learning Godot and learning that they use .NET for the C# runtime is a nice touch. I write a lot of code that targets .NET in my day job, so having to learn the unity way of doing things would be frustrating.
Good article but seems strange that author benchmarked debug builds first, that’s a huge “no-no” in any perf tweaking and it’s clear that authors knows this well
> In 2018, Unity engineers discussed that they are working on porting the engine to .NET CoreCLR

Hard task, no doubt. Unity needs to throw everything at this problem. C# in general has gotten insanely fast by default. It's very much worth taking the time to upgrade/update.

Whilst we don't compare in size and api surface, it took us a few months to get off 472 and onto dotnet6. But once we were on dotnet6, moving to the LTS after that was relatively painless; usually a few hours of work.

Unity has a unity problem.

While it’s easy to get in and make something (it’s got all the bells and whistles) it also suffers from the monolith problem (too many features, old code, tech debt).

The asset store is gold but their tech feels less refined. It’s leaps and bounds where it was when it started but it still has this empty feel to it without heavy script modifications.

There is the problem. The scripting ending designed around mono doesn’t translate as well to CoreCLR and using the same Behavior interface gets a little more complicated.

There are times (even with my own engine) that one must let go of the old and begin a new. Dx7->dx9, dx9->opengl, opengl->vulkan, vulkan->webgpu.

EDIT I was just thinking, returning to this a couple of minute later, that if Unity wanted to prove they really care about their Core, they would introduce a complete revamp of the editor like Blender did for 3.X. Give more thought to the level editors and prefab makers. Give different workflow views. Editing / Animation / Scripting / Rendering / Post

As it stands now, it’s all just a menu item that spawns a thing in a single view with a 1999 style property window on the side like Visual Studio was ever cool.

The biggest problem IMO is that they never finish new features. They start the work on implementing new tech/assets/plugins, then abandon them halfway through just as they are becoming useful for prod. There are tons of would-be-amazing tools but they are all stuck at "0.x-preview" versions, and eventually after 5-10 years either stop working completely or are over-shadowed by newer, shinier assets, which often re-invent the wheel and/or do things worse than the previous attempt. I stopped trying out new tech until its 1.0 (or preferably later, 2.0+ is safer) because I'm afraid to be bitten (again) becoming dependent on abandoned plugins and have to at some point update to something else. It's a lose-lose-lose proposition: Unity throws away time and money re-inventing plugins, we throw away time and money having to port to functioning tools, customers lose time because they get to discover bugs caused by outdated plugins/assets that cause weird errors that are hard to track down.
The author (probably unknowingly) glosses over a lot in these sentences of the "How did we get here" section:

> Unity uses the Mono framework to run C# programs and back in 2006 it was one of the only viable multi-platform implementations of .NET. Mono is also open-source, allowing Unity to do some tweaks to better suit game development. [...] An interesting twist happened nearly 10 years later.

Not mentioned is that Mono itself of course improved a lot over the years, and even prior to MS's about-face on open source, it was well known that Unity was hindered by sticking with an old and out-of-date Mono, and they were very successful at deflecting the blame for this by throwing the Mono folks under the bus. Any time complaints about game developers' inability to use newer C# features came up, Mono/Xamarin would invariably receive the ire* because Unity couldn't come to an agreement with them about their license and consulting fees. (Mono was open source under LGPL instead of MIT licensed at the time, and Unity had initially bought a commercial license that allowed them exemptions from even the soft copyleft provisions in the LGPL, but the exemption was limited and not, for example, for any and all future versions, too, indefinitely.) Reportedly, they were trying to charge too much (whatever that means) for Unity's attempts to upgrade to the modern versions.

It's now 10+ years later, and now not only Mono (after being relicensed under MIT) but .NET CoreCLR are both available for Unity at no cost, but despite this it still took Unity years before they'd upgraded their C# language support and to a slightly more modern runtime.

Something else to note: Although, LGPL isn't inherently incompatible with commercial use or even use in closed source software, one sticking point was that some of the platforms Unity wished to be able to deploy have developer/publisher restrictions that are incompatible with the soft copyleft terms in the LGPL that require that users (or in this case game developers) be allowed to relink against other versions of the covered software (including, for example, newer releases). Perversely, it's because Unity sought and obtained exemptions to the LGPL that both end users and game developers were hamstrung and kept from being able to upgrade Mono themselves! (It wouldn't have helped on, say, locked down platforms like Nintendo's for example, but certainly would have been viable on platforms without the first-party restrictions, like PC gaming or Android.)

By now, Unity has gone on to pull a lot of other shenanigans with their own pricing that seems to have sufficiently pissed off the game development community, but it should still not be forgotten when they were willing to pass the blame to an open source project over the development/support that the company felt it was entitled to for a price lower than they were told it would cost, and that they themselves were slow to make progress on even when the price of the exemption literally became $0.

* you can find threads with these sorts of comments from during this period right here on HN, too, but it was everywhere

In general when game development comes up here I tend not to engage as professional gamedev is so different than what other people tend to deal with that it's hard to even get on the same page, but seeing as how this one is very directly dealing with my expertise I'll chime in.

There are few things off with the this post that essentially sound as someone more green when it comes to Unity development (no problem, we all start somewhere).

1. The stated approach of separating the simulation and presentation layers isn't all that uncommon, in fact it was the primary way of achieving performance in the past (though, you usually used C++, not C#).

2. Most games don't ship on the mono backend, but instead on il2cpp (it's hard to gauge how feasible that'd be from this post as it lacks details).

3. In modern Unity, if you want to achieve performance, you'd be better off taking the approach of utilizing the burst compiler and HPC#, especially with what appears to be happening in the in sample here as the job system will help tremendously.

4. Profiling the editor is always a fools errand, it's so much slower than even a debug build for obvious reasons.

Long story short, Unity devs are excited for the mentioned update, but it's for accessing modern language features, not particularly for any performance gains. Also, I've seen a lot of mention around GC through this comment section, and professional Unity projects tend to go out of their way to minimize these at runtime, or even sidestep entirely with unmanaged memory and DOTS.

It blows my mind that Unity has not been able to migrate to .NET (CoreCLR) after starting back in 2018.

I would have estimated a year, or two tops, for that project.

CoreCLR doesn't help on console platforms because you can't ship the JIT runtime. To my knowledge CoreCLR's AOT solution won't work because of SDK and build requirements for shipping builds on consoles. I believe some consoles require that all shipped native code must have been compiled with the SDK compiler. Even if you fork the CoreCLR AOT system so you can build for the consoles (the code can't be open because of NDAs) you wouldn't be allowed to ship the binary. IL2CPP is the only path forward there. CoreCLR is only viable on PC.
My dream would be that I can adb into my phone, install the .Net SDK or .Net Runtime (v 8 or 10) and have my applications run natively on Android. Simple console apps first, then the rest. Google should open their platform up a little bit more. Allow us to enable root access via adb. Let us unleash our pocket computer's full potentials. Would love to have portable low powered servers, running stacks of my choice. They are super efficient already, have large storages, I can already plug in a usb-c hub into my phone to get more storage, LAN network, keyboard/mouse, external mic if needed. Running my phone with wireguard/tailscale + some lightweight containers = awesome!

Unity really do need to catch up though. .Net 8/10 is really amazing and FAST. With the new garbage collector changes for .Net 10, game stutter would be nearly gone gone.

Unrelated point: I basically stream most of my games with Sunlight + Moonlight from my main rig to my phone with a controller attached, so I can play Diablo 2 remastered, Hades, Grim Dawn and basically any game with Xbox controller support, directly on my phone. Works amazingly well. Phones has high dpi screen + 120hz OLED panel and since all rendering happens on my headless gaming server, at highest settings, the phone's battery doesn't really drain at all, can play for hours. Playing a Mono based game from the play store can drain the battery within 2 hours, and the games are usually so badly built for android, even paid games that have equivalents on steam.

I believe the root constraints are a security thing. They don't want you to charge your phone in a dodgy USB outlet in a public space (e.g. an airport) and be compromised. Making you flash your phone to get root is meant to prevent people accidentally exposing themselves.
Google can provide a non-rooted system image out of the box for safety, but nothing stops them from providing a clean & rooted image for download that I can flash onto device if needed.

The security model should already be tight enough so that a dodgy usb outlet does not compromise the core system (rewriting system images).

Ah I wonder if this could've saved me countless hours of optimizing my VR game Rogue Stargun for the Quest 2, particularly the final battle in the game
my main problem with Unity games is never the speed - it's the outrageous RAM usage
Does anyone on here know why it is that Unity doesn't just run on .NET? The cross-platform benefits of Mono have disappeared a decade ago or something like that.

Like, why would Unity invest in hacks like il2cpp rather than just port over to modern .NET? Is there some licensing thing going on?

Sunk cost fallacy will be a big factor. They already invested a lot of money/time into customizing Mono and hacks like Burst and IL2CPP, so there's momentum to "stay the course" and continue investing in those. Even if some evidence suggests that it is the wrong course.
Not to mention the Hot Reload which comes out of the box.
I wrote the Circuits programming system for Rec Room, and we took a similar approach: keeping the core logic free of Unity dependencies so it could run and be tested on CoreCLR.

The results were similar as well with significantly better performance outside of Unity. There are also secondary benefits. Unity’s API surface is huge, and it’s easy for code to accidentally entangle itself with engine details in ways that confuse or constrain other developers.

By isolating the core in a separate DLL, we only expose Unity concepts where they’re actually needed, and we enforce that boundary with tests. That logic constraint ended up being just as valuable as the performance gain

It's cool to see detailed traces and flame graphs be used more often! A lot of different problems could be detected if they were available for pretty much any language, with enough details and tooling to be useful. Heh, I remember also using VisualVM for finding issues with a web app HTTP thread pool and later with the SQL queries being executed (and also the DB pooling solution).
I wonder why the author doesn't use IL2CPP and sticks to Mono. IL2CPP does produce much faster code, making Mono builds obsolete. This should be the very first step you do if you care at all about performance in Unity.
Wanted to chime in here with some thoughts/clarifications as I'm the Enginnering Manager of the VM team at Unity, aka the team that is leading the charge on .NET Modernization and the CoreCLR transition (and also owns IL2CPP). Also speaking here as myself and obviously not on behalf of the company.

First thing is that CoreCLR is _very_ much an active development effort and we're committed to the roadmap we presented at Unite, with at least a CoreCLR-backed Player (aka, what you "Build" when you build your game with Unity) being delivered as a technical preview around the 6.7 release timing. This would basically mean being able to select "CoreCLR" as a scripting backend option (similar to IL2CPP) and see some of the upside and benefit the author mentions here.

That said, Unity isn't a pure C# environment. As lots of people know, there is a robust native layer underlying a lot of the managed (C#) code operating a pseudo-ECS design (not literally DOTS/Entites but an engine architecture thing). This means that a lot of the load-bearing code Unity-the-engine is running every frame is notably _not_ C# code, but instead native code that is, in a lot of cases, already very fast. This means that for tight loops of certain systems in the engine, moving to modern .NET isn't going to carry an implict performance increase of those systems. Said differently, CoreCLR isn't a magic performance bullet for Unity. What we like to say though is that "CoreCLR will make C# code faster", so if your game (or general scripting architecture like the author brings up, with lots of "loose" C#) is very C# dependent, you _will_ see a lot of benefit.

One thing we starting to investigate is how much performance there is to gain in Unity-the-engine by migrating legacy native C++ code to C# backed by CoreCLR. C# code can be a lot more maintainable and I'd be lying if I said that we really need _every_ managed->native->managed jump we do in the engine, especially with the performance benefit CoreCLR gives us. There are additional things as well like getting intrinsic-backed (or JIT'd) SIMD code for any C# we write with apis like Span<T>, covering for plenty on places in native code where we aren't directly checking for processor arch at runtime or the compiler misses some optimization. This is especially relevant as we also move the editor to CoreCLR, and obviously want it to be as fast as possible, and represents some of the attendant benefits of really focusing on .NET modernization.

Regardless, CoreCLR is very much the future of Unity and we're all very excited about it and the progress we're making. The player in 6.7 is the first step and there are lots of other parts (like modern C# lang versions) that will be major boons to development writ large. I (personally, as a game developer) see a lot of awesome things possible downstream of the shift and am (again, selfishly) very excited about what's possible.