- Unityが使用するMonoランタイムは、最新の.NETと比べて実行速度が著しく遅く、同じC#コードでも最大15倍まで差が出る事例がある
- 実際のゲームコードで、MonoベースのUnity実行は100秒、同一コードの.NET実行は38秒と計測されており、デバッグやテストの効率にも大きく影響する
- ReleaseモードでもMonoは30秒、.NETは12秒で、最適化された環境でも2.5倍以上の性能差が維持される
- 原因はMonoの非効率なJITコンパイルとインライン化の失敗、過剰なメモリコピーなどで、.NETの最新CoreCLR JIT最適化と対照的である
- UnityがCoreCLRベースの.NETモダナイゼーションを完了すれば、ゲームとエディタの両方で大きな性能向上が可能となり、これはすべてのUnityプロジェクトにおける隠れた性能税の解消につながる見込み
UnityがMonoを使う背景
- Unityは2006年からMonoフレームワークを使ってC#コードを実行している
- 当時のMonoは唯一のマルチプラットフォーム.NET実装であり、オープンソースだったためUnityが改変可能だった
- 2014年以降、Microsoftは**.NET Coreをオープンソース化し、2016年に.NET Core 1.0**をリリース
- 以後、Roslynコンパイラ、新しいJIT、性能改善など、.NETエコシステムは急速に発展した
- 2018年、UnityのエンジニアはCoreCLR移植作業を進めていると明かし、Mono比で2〜10倍の性能向上を期待していた
- しかし2025年末の時点でも、CoreCLRベースのゲーム実行は不可能な状態にある
Monoと.NETの性能格差
- UnityプロジェクトのシミュレーションコードをUnity外部で.NETとして実行し、直接比較
- Unity/Mono環境: 100秒、.NET環境: 38秒(Debugモード基準)
- ReleaseモードではMono 30秒、.NET 12秒で差が維持される
- .NETは4K×4Kマップを3秒以内に生成するなど、マルチスレッド最適化に優れる
- Monoの非効率なコード生成が主因で、単純なループでも15倍の速度差が発生する
アセンブリ比較: Mono vs .NET
- 同じテストコードから生成されたx64アセンブリを比較した結果
- .NET JITはループ不変式をループ外へ移動(hoisting)し、最小限のレジスタ演算だけを実行
- Monoは数十個のmov命令でメモリコピーを繰り返し、非効率なインライン化によって性能が低下する
int.MaxValue反復ループの実行時間
- .NET: 750ms、Mono: 11,500ms、Unity Editor(Debug): 67,000ms
- Monoはループ内で不要なメモリ移動と比較演算を繰り返し実行する
CoreCLR導入の意味
- CoreCLRは最新JIT、Span<T> API、SIMD最適化、ハードウェア命令サポートなどの現代的な機能を提供
- これらの機能により、さらに2倍以上の性能向上の可能性がある
- UnityのBurstコンパイラはLLVMベースでネイティブコードを生成するが、C#機能の制限が存在する
- CoreCLRの現代的なJITはBurstに近い性能を提供しつつ、言語上の制約が少ない
- CoreCLRは**AOT(事前コンパイル)をサポートし、起動速度の改善やJITが制限されるプラットフォーム(iOSなど)**への対応も可能
- ただしUnityは依然としてIL2CPPを維持する方針を示している
結論: Unityに必要な.NETモダナイゼーション
- Monoは最新.NETと比べて1.5〜3倍以上遅い実行性能を示し、これはすべてのUnityプロジェクトにおける隠れたコストとして作用する
- CoreCLR導入時に期待される効果
- ランタイム性能向上、高速な反復ビルド、GC改善、ドメインリロードの削減、マネージドコード比率の拡大
- Unity 6.xのロードマップには**.NET Modernizationが含まれているが、予定は2026年以降**となっている
- CoreCLRサポートが完成すれば、Unity開発者とプレイヤーの双方に実質的な性能革新をもたらす可能性がある
- 現時点では、Monoの限界がUnityエコシステム全体の性能ボトルネックとして残っている
13件のコメント
ああ……Mono はまだ legacy な .NET Framework ベースなんですね……
ゲームではなく、10万行くらいの .NET 4.8 + LINQ to SQL + WinForms の金融アプリを .NET 10 + Entity Framework に移行している途中ですが、かなり速くなったのを実感しています。10秒かかっていた計算処理が3秒に縮むことも!
NuGet互換も追加してくれるとうれしいです(私がUnityにあまり詳しくないからでしょうか?)
公式サポートではありませんが……
NuGetForUnityというオープンソースはあります。理論上は、.NET Standard 2.0 をターゲットにして作られた NuGet パッケージは Unity 環境でも読み込んで使えることにはなっている……のですが、やはり不便な点は多いように思います。
https://learn.microsoft.com/ko-kr/dotnet/…
もっともな話ではあるけれど、あえてエディタのパフォーマンスを比較する理由はいまいちよく分かりませんね……。せめてデバッグビルドでも持ってきて比較していたらどうだっただろう? いや、そうするとかえって説得力が落ちたのかな? 一方で、IL2CPP も Mono もどちらも時代遅れの技術であることには変わりない気もするけれど
大規模なプロジェクトでは、エディタのパフォーマンスが開発体験を大きく損なっているため、エディタ性能にも意味があります。エディタの起動が遅く、アセットのインポートも遅く、デバッグ/テストのループも遅く…。
ああ……もちろんこれも重要ではあります。私が最初に読んだとき、記事の筆者はもう少し根本的なコード実行速度について論じたかったようにも見えました。Unity はおっしゃるとおり、エディタも遅く、インポートも遅く、全体的なテストループが遅いというのも事実ですね……
Unityに関する記事を見ると、とてもうれしいですね。
興味深く拝読しました。
うまく導入されれば、ありふれたインディーゲームの最適化はおそらく良くなるだろうと予想…
Mono が必ず CoreCLR によってモダン化されるべきもう一つの理由として、Unity には Mono の性能改善に投資する余力も意思もあまりないだろう、という点があると思います。.NET Framework 時代の遺産は、一日でも早く整理されるのが正しいと思います。 :-D
そして、.NET 10を節目として、かつてIL2CPPで解決しようとしていた問題が、別の展開方向ではあるものの、的確に扱われつつある点(Native AOT)も考慮されるとよいのではないかと思います。
もちろん、これは途中で編集可能なC++コードが生成されないという限界はありますが、結果としてJust-In-Timeが発生しないネイティブバイナリの生成は、.NET 8を皮切りに、10に至ってさらに成熟してきました。
そうした理由から、CoreCLRへのモダナイズをこれ以上先延ばしにするのは、Unityにとって良い選択にはならないだろうと思います。あるいは、まったく別の言語や基盤へ切り替えるほうが、より有効かもしれません。
> UnityはMonoのパフォーマンス改善に投資する余地も意欲もあまり高くないでしょう
これにも強く同意します...
Hacker News のコメント
記事には、Unity 開発経験があまりない人が書いたように見える部分がいくつかあった。
要するに、Unity 開発者が今回のアップデートに期待しているのは性能向上よりも 最新の言語機能へのアクセス のためだ。そして実行中の GC を最小化したり、非管理メモリ や DOTS で回避したりするのが一般的だ。
IL2CPP は .NET IL を C++ に変換する 品質の低いコードジェネレータ にすぎず、最適化コンパイラ頼みだ。
Unity ブログの IL2CPP 内部構造を見ればわかる。
Burst/HPC# も ECS や SoA のようなトレンドには従っているが、性能はよく書かれた C++ や CoreCLR の C# に及ばない。
しかもこれらの技術は クローズドで Unity 専用 なので外部では使えない。Unity はいつも遅い Mono との比較ベンチマークでマーケティングしている。
結局 Unity も CoreCLR を受け入れざるを得なくなり、そのとき既存の複雑なコードよりも 普通の C# コードの方が速いという現実 に気づくことになるだろう。
IL2CPP を使わないのは、ランタイム DLL ロード、リフレクション、FieldOffset 構造体パッキング などと互換性がないためだ。
モッダーが IL インジェクションで機能を拡張できるので、結果として開発速度が上がる。
Burst と HPC# は複雑さと制約が多く、好まない。Mono と .NET の性能差のせいでなおさらもどかしい。
エディタでのプロファイリングも、実際のビルドと似た比率で性能向上を示してくれるので有用だった。ただし Unity 標準のプロファイラは不正確なので、自作のトレースシステム を使っている。
GC は依然として問題だ。文字列処理や UI が毎フレーム ガベージ を生成する。CoreCLR になれば、より良い API と移動型 GC によってメモリ断片化の問題を減らせるはずだ。
Asset Store は素晴らしいが、エンジン自体は洗練されていない印象だ。
Mono ベースのスクリプティングは CoreCLR へ移行するには構造的に複雑すぎる。
Unity が本当に Core を改善したいなら、Blender 3.x のようにエディタ全体を再設計 しなければならない。
今のままでは 1999 年製の UI のように感じる。
数多くのプラグインやツールが「0.x-preview」の段階で止まり、5~10 年後には動かなくなるか、新しいアセットに埋もれてしまう。
だから私は今では 1.0 以上のバージョンしか使わない。そうしないと放棄されたプラグインに依存することになり、結局また移植し直す羽目になる。
Unity にとっても、開発者にとっても、ユーザーにとっても損だ。
内部的に 自社ゲーム開発の失敗 があり、実際のゲーム制作感覚が不足している。
ただ要望された機能を追加しているだけで、一貫したビジョン がない。
性能が重要なら Vulkan を直接呼ぶべきで、移植性 が重要なら WebGPU を使うべきだ。
ブラウザごとに実装が違うためオーバーヘッドが生じるが、OS ドライバレベルで WebGPU が提供されれば解決できる。
一方で Godot は シンプルで意味のあるビルディングブロック を提供してくれるので、欲しいものを自由に作れる。
Asset Store も同様にバージョン互換性の問題で保守が難しく、ほとんどが 放置されたアセット になってしまう。
Unity が有用なアセットを買収しても、統合はまともに行われず、競合アセットは消えてしまう。
一方 Unreal Engine はこうした機能を エンジン内蔵レベル で提供している。
Unity は IL2CPP により良い GC を導入する予定もない。
CoreCLR ベースのエディタが出れば、むしろ エディタの方がビルドより速い可能性すらある。
関連する議論: Unity CoreCLR と .NET モダナイゼーション
インクリメンタル GC がうまく動けば、スタッターの問題もそれほど大きくない。
C# 自体が非常に高速になっているのだから、Unity はこの移行に 全力を注ぐべき だ。
私たちのチームは .NET Framework 4.7.2 から .NET 6 への移行に数か月かかったが、その後の LTS バージョンアップグレードは 数時間程度 で済んだ。
遅延は続き、リーダーたちは去っている。
代案として .NET 10 ベースの Stride エンジン を勧めたい。Unity のような 境界オーバーヘッド がない。
Godot はオープンソースだが C# サポートが不安定で、Web ビルドができないなら ゲームジャム向きではない。
GPU サポートのある 本物のサンドボックスソリューション が必要だ。
優先順位が変わり続け、要件も修正されるため、作業が繰り返し発生する。
開発者たちは今でも優秀だが、一貫した推進力 が足りない。
こうした大規模なリライトは、CEO にとっても リスクの高い決断 だ。
その結果、性能が大きく向上し、エンジン依存性の低下 によってコード保守も楽になった。
Unity の概念を必要な箇所にだけ露出させ、テストで境界を強制することで、論理的分離の価値 を実感した。
ルート権限があり、WireGuard や Tailscale のようなネットワークツールと組み合わせれば、携帯型サーバー としても完璧だ。
.NET 10 の新しい GC でゲームのスタッターもほとんど消えるはずだ。
今はメイン PC から Sunlight + Moonlight でゲームをストリーミングして、スマホで遊んでいる。
高リフレッシュレートの OLED 画面のおかげで、バッテリー消費も少ない。
.NET SDK そのものではないが、Mono ランタイムがアプリに同梱 されているので、体感としては近い。
Mono の クロスプラットフォーム上の利点はすでに失われている のに、なぜ IL2CPP のような複雑なハックを維持しているのか理解できない。
長年にわたる 非標準の修正 が積み重なっていて、大企業でなければ新たに最適化するのも難しい。
この規模のプロジェクトなら 1~2 年で十分だったはず だと思う。