ハクソク

世界を動かす技術を、日本語で。

UnityのMono問題:なぜあなたのC#コードは期待よりも遅く動作するのか

概要

  • UnityのMonoランタイムは、現代の.NETと比較して大幅に遅い
  • .NET CoreCLRへの移行で、2~15倍の速度向上が期待できる。
  • ゲームのビジネスロジックを分離し、直接比較したベンチマーク結果を紹介。
  • CoreCLR導入は、パフォーマンスと開発効率の大幅な向上をもたらす。
  • しかし、Unityの.NETモダナイゼーションはまだ生産環境向けではない状況。

UnityのMonoランタイムと.NETのパフォーマンス比較

  • UnityはMonoフレームワーク上でC#コードを実行。
  • 2006年当時、マルチプラットフォーム対応の.NET実装としてMonoが唯一の選択肢だった。
  • 2014年以降、Microsoftが.NETをオープンソース化し、.NET CoreCLRが登場。
  • .NET CoreCLRはパフォーマンスと機能面で大幅な進化を遂げている。
  • 2018年からUnityもCoreCLR対応を進めているが、2026年まで本格運用は未定

ベンチマーク結果

  • ゲームのシミュレーションコードをUnity/Monoと**.NET**で比較。
    • Unityエディタ(デバッグ): 100秒
    • .NETユニットテスト(デバッグ): 38秒
  • スタンドアロン(リリース)での比較
    • Mono(リリース): 30秒
    • .NET(リリース): 12秒
  • .NETの方が2~3倍速いだけでなく、一部ワークロードでは10倍以上の差も観測。

パフォーマンス差の要因

  • MonoのJIT(Just-In-Time)コンパイラが最適化不足。
  • .NETは最新のJIT最適化技術を活用。
  • ベンチマークコードでは、Monoは式の単純化やインライン化が苦手で、.NETはループ外への最適化ができている。

Unityの.NETモダナイゼーションの意義

  • CoreCLR導入でゲーム本体やUnity Editorのパフォーマンスが飛躍的に向上
  • 新しいC#機能や**Span<T>、ハードウェア命令(SIMD)**などの最適化APIが利用可能に。
  • デバッグやテストの効率化ドメインリロード不要化GC(ガベージコレクション)の改善など、開発体験も向上。
  • BurstのようなJIT最適化もCoreCLRでほぼ実現可能。
  • iOSなどJIT禁止環境では、**IL2CPPによるAOT(事前コンパイル)**が引き続き採用される見込み。

現状の課題

  • Unityは8年以上.NETモダナイゼーションに本腰を入れてこなかった
  • CoreCLRのAOTサポートは計画なし、引き続きIL2CPPが主流。
  • CoreCLRへの全面移行はUnity 6.xロードマップに記載されたが、リリース時期は未定。

Monoと.NETのJIT最適化比較(技術詳細)

  • テストコード:カスタム構造体の加算処理を大量ループで実行。
  • .NET JIT生成コード
    • ループ不変式の外出し加算命令の最適化を自動で実施。
    • int.MaxValue回のループが約750msで完了
  • Mono JIT生成コード
    • 余計なメモリアクセスや命令が多数含まれる。
    • ループ内での無駄なデータ移動・計算が多く、パフォーマンス低下の要因。

まとめと今後の展望

  • Monoは.NETに比べて大幅にパフォーマンスが劣ることが明確。
  • CoreCLR対応で、実行速度・開発効率・新機能活用が大きく前進。
  • Unityの.NETモダナイゼーションが本格化すれば、業界全体に恩恵
  • それまでは、見えないコストとしてMonoの低速さがUnity開発者を悩ませ続ける状況。
  • Unity開発者・ユーザーはCoreCLR導入を強く期待

要点

  • Unityの現行Monoランタイムはパフォーマンス面で時代遅れ
  • .NET CoreCLR移行で、2~10倍超の高速化が期待できる。
  • 新機能・最適化APIの活用、開発効率向上も大きなメリット。
  • 2026年以降にUnity 6.xでCoreCLR正式対応予定、今後の動向に注目。

Hackerたちの意見

うーん、UnityはもうCoreCLRへの移行に必要な技術力がない気がする。遅れ続けてるし、技術リーダーもどんどん辞めてるしね。https://github.com/stride3d/stride なんかどう?もう.NET 10に対応してるし、Unityみたいにクロスバウンダリーのオーバーヘッドもないよ。
進捗は遅々としてるけど、Unityは前に進んでるみたい。Unityの計画と進捗についての更新: 2022年 - CoreCLRへの移行を公式に発表 - https://unity.com/blog/engine-platform/unity-and-net-whats-n... 2023年 - テクニカルアップデート - https://unity.com/blog/engine-platform/porting-unity-to-core... Unite 2025 - CoreCLRベースのプレイヤーが2026年のUnity 6.7に予定されてる - https://digitalproduction.com/2025/11/26/unitys-2026-roadmap...
StrideはUnityの機能のほんの一部しかないよ。Godotが唯一の本当のオープンソースの競争相手だけど、C#のサポートがイマイチなんだよね。Webにビルドできないなら、ゲームジャムには役立たないし、誰もランダムなバイナリをダウンロードして実行するべきじゃないからね。本当にGPUサポートのあるサンドボックスソリューションが必要だよ。
購入した「アドオン」のサポートを削除して書き直すことを、買い物をした人たちに伝えなきゃいけないと想像してみて。そしたら、株主や顧客に「wecan」の価値の低下と、同じ機能を回復するためのコストを説明しなきゃいけない。買収した企業の技術の寄せ集めは、どんな技術リーダーにとっても厄介な問題で、書き直しはCEOのキャリアを終わらせることになる。
記事には書いてないけど、Unityが使ってるGCは.NETやスタンドアロンのMonoに比べてパフォーマンスがすごく悪いんだ。Boehm GCを使ってるからね。最後に聞いたとき、UnityはIL2CPPをもっと良いGCに切り替える予定はないみたいだよ。[1] CoreCLRエディタのパフォーマンスがどうなるか楽しみだね。これだけ速度差があると、エディタで動かす方がスタンドアロンのMono/IL2CPPビルドよりも良くなるかもしれない。
> Boehm GCを使ってるからね。 なんで?Monoはかなり良い精密GCを持ってるのに、もう何年も前から。
エディタのスピードアップについてだけど、「ドメインリロード」ってやつを完全に排除できるはずだよ。C#が変更に応じてアンロードされて再ロードされる必要があるからね。
一方で、より良いGCは確かにいいけど、もう一方ではそれほど重要じゃないかも。フレームごとのアロケーションはゼロにしたいし、それは多分変わらないだろう。あまり頻繁に発生しないガベージがインクリメンタルGCを上回らなければ、特に問題にはならない。意図通りにインクリメンタルに動いていれば、カクつきも問題ないはず。ゲームでは、サーバーインスタンスのように常にリクエストを増やせるわけじゃないから、GCのスループットが無限に良くなるわけじゃない。
最近Godotを学び始めたんだけど、C#ランタイムに.NETを使ってるのはいいね。仕事で.NETをターゲットにしたコードを書くことが多いから、Unityのやり方を学ぶのはちょっとストレスだよ。
> 2018年にUnityのエンジニアたちがエンジンを.NET CoreCLRに移植する作業をしていると話してた。 確かに大変な作業だね。Unityはこの問題に全力を注ぐ必要があるよ。C#はデフォルトでめちゃくちゃ速くなってるから、アップグレードする価値はすごくある。私たちはサイズやAPIの面では比較にならないけど、472からdotnet6に移行するのに数ヶ月かかった。でも、一旦dotnet6に移ったら、その後のLTSへの移行は比較的楽だったよ。通常は数時間の作業で済んだ。
Unityには統一性の問題があるね。何かを作るのは簡単だけど(機能は盛りだくさん)、モノリス問題にも悩まされてる(機能が多すぎて、古いコードや技術的負債がある)。アセットストアは素晴らしいけど、技術的にはちょっと洗練されてない感じ。始まった頃から比べれば格段に進化したけど、重いスクリプトの修正なしではなんか空虚な感じがするんだよね。それが問題なんだ。モノに基づいたスクリプトの設計はCoreCLRにうまく移行できないし、同じBehaviorインターフェースを使うのも少し複雑になってる。時には(自分のエンジンでも)古いものを手放して新しいものを始める必要がある。Dx7からdx9、dx9からopengl、openglからvulkan、vulkanからwebgpu。追記:数分後に考えたんだけど、もしUnityが本当にコアを大事にしてるなら、Blenderが3.Xでやったみたいにエディタを完全にリニューアルするべきだと思う。レベルエディタやプレハブメーカーにもっと考慮を払って、異なるワークフロービューを提供してほしい。編集 / アニメーション / スクリプティング / レンダリング / ポスト。今のままじゃ、1999年スタイルのプロパティウィンドウが横にあるだけの、単一ビューで物を生成するメニュー項目に過ぎない。
最後のステップは意味不明だね。WebGPUはVulkanに似たシムレイヤーで(WebGLがGLESに似ているのと同じように)、OSのネイティブGPGPU時代のAPIを使えるようにしてくれる。適切なOSでは、WebGPUはすべての呼び出しをVulkanに1:1で変換して、かなり安価に処理してる。Windowsでは、ブラウザがGPUベンダーによってこれを行うけど、NvidiaはVulkanのパフォーマンスがあまり良くないし、DX12と同じパフォーマンスが出るべきところでもそうじゃない。AMDはこのバグに悩まされていない。パフォーマンスを気にするなら、Vulkanを直接呼び出してオーバーヘッドを払う必要はない。ポータビリティを気にするか、WASMターゲットにコンパイルするなら、ほぼWebGPUに制限されて、そのペナルティを払わなきゃいけない。余談だけど、WindowsドライバーやLinuxのMesaがWebGPUの実装を提供するのを妨げるものはないから、ブラウザはそういったドライバーに独自のシム実装を必要とせず、翻訳オーバーヘッドも発生しないはずなんだ。でも、実際にはそうなってない。
そうだね、前にUnityでプロジェクトを始めて、その間にGodotも試してみたんだ。Unityは特定のことをするのに正しい方法が一つあるべきだって感じるけど、実際には使い方によってはそれがうまくいかないから、回避策を考えなきゃいけない(基本的にUnityの機能ごとにこれを繰り返すことになる)。一方、Godotは本当に意味のあるシンプルなビルディングブロックを手渡してくれる感じがする。
Unityの大きな問題は、彼らが方向性を失っていることだと思う。プラグインを買ってランダムな機能を追加するだけで、実際には箱にもっとステッカーを貼るためだけのサービスになってる。自分たちのゲームを作ろうとして失敗してるし、どうやってゲームを作るかも分かってない。彼らはただ人々が求めることを空白の中で聞いて、それを出荷してるだけ。Unityには才能ある人がたくさんいるけど、すぐに大きな変化があるとは思えないな。
Unityは毎年、自分たちの機能のAPIを壊すのが得意で、そのせいで自分たちのチュートリアルが機能しなくなる。昔からあるしっかりしたベースラインAPIがあって(制限も知られてる)、レガシーレンダーパイプラインみたいなものもある。改革しようとするたびに混乱や複雑さが生まれて、実験的なものとサポートが終了したものの間にある。Asset Storeについては同じ理由で君とは意見が合わない。壊れる頻度が高いから、常に更新されていないものはもう機能しなくなるし、並行するエンジンバージョンのために複数のバージョンを維持する必要がある。それに加えて、Asset Storeの経済性も疑わしい(これらのものを作るのはお金の面で意味がないと思うし、維持するのはなおさら)。ほとんどが放置されたソフトウェアになっちゃう。そして、Asset Storeで必要不可欠なものを作ると(大抵はエンジンが最初から持っているべきもの、例えばまともなテキストレンダリングみたいな)、次のことが起こる: - Unityが買収して、資産をエンジンに組み込むけど、統合作業は何もしないから、目立っちゃう(TextMeshPro)。君には良いけど、消費者には悪いし、競合を作ってたら最悪。 - Unityが社内ソリューションを作るけど、君は当然競争できないし、エンジンへのアクセスがあるから大きなアドバンテージを持ってる(君は辛い立場になる)。 - エンジンはその機能を持たないままで、「買えばいいじゃん」となるから、質が疑わしい資産に何百ドル/ユーロも使うか、使い勝手がさらに異なるオープンソースのバージョンを探さなきゃいけない。UE4/5には多くのこれが組み込まれていて、AAAクオリティだよ。
> 1999年スタイルのプロパティウィンドウがサイドにあるなんて、Visual Studioがかつてクールだったとは思えない。 それは公平じゃないと思う。Unityのインスペクタウィンドウは、単なるプロパティウィンドウじゃなくて、良い部分の一つだと言えるよ。即時モードのUIで、プロパティ編集だけじゃなくて、もっと多くのことにフックできるんだ。
ゲーム開発の話が出ると、プロのゲーム開発は他の人が扱うこととは全然違うから、あまり関わらないことが多いんだけど、これは自分の専門に直接関係してるから、ちょっとコメントするね。この投稿にはいくつかおかしな点があって、Unity開発に関してはまだ経験が浅い人のように聞こえる(問題ないよ、みんな最初はどこかから始めるから)。1. シミュレーション層とプレゼンテーション層を分けるアプローチはあまり珍しくない。実際、過去にはパフォーマンスを達成するための主要な方法だった(ただし、通常はC++を使ってたけど、C#ではない)。2. ほとんどのゲームはモノバックエンドで出荷されることはなく、代わりにil2cppで出荷される(この投稿からはその可行性を測るのは難しいけど、詳細が欠けてる)。3. 現代のUnityでは、パフォーマンスを達成したいなら、バーストコンパイラとHPC#を利用するアプローチが良いよ。ここで見えるサンプルの状況では、ジョブシステムが大いに役立つはず。4. エディタのプロファイリングはいつも無駄な努力だね。明らかな理由で、デバッグビルドよりもずっと遅いから。要するに、Unityの開発者たちは言及されたアップデートにワクワクしてるけど、それは現代の言語機能にアクセスするためで、特にパフォーマンス向上のためではない。あと、このコメントセクションではGCについての言及が多いけど、プロのUnityプロジェクトは実行時にこれを最小限に抑えるために努力するか、管理されていないメモリやDOTSで完全に回避する傾向があるよ。
ここでの著者です。あなたの視点に感謝します。いくつかの考えを述べますね。> シミュレーション層とプレゼンテーション層を分けるアプローチはあまり珍しくない。ある程度の分離は珍しくないと思いますが、ゲームは通常、それぞれのエンジンからのものに依存します。特にデータ型(例:Vector3)や数学ライブラリのようなものに。私たちのゲームがこの点でユニークだと言う理由は、描画しないコードがUnityの型やDLLに依存していないからです。これはUnityで作られたゲームとしてはかなり珍しいと思います。> ほとんどのゲームはモノバックエンドで出荷されることはなく、代わりにil2cppで出荷される。これについては本当に依存すると思います。絶対数で見ると、SteamのUnityゲームの約20%がIL2CPPを使用しています[1]。もちろん、多くのシンプルなゲームはそれを使用していないので、サンプルが歪んでいますが、「IL2CPP技術を持つゲームをプレイするプレイヤーの数」を測るには影響があります。しかし、依然として多くのゲームがあり、管理されたコードのパフォーマンス向上は確実に影響を与えるでしょう。私たちはIL2CPPを使用しないのは、それに互換性のない多くの機能を使っているからです。例えば、DLLを介してランタイムでのDLCやモッドの読み込み、カスタムシリアル化のためのリフレクション、効率的な構造体のパッキングやGPU通信のための[FieldOffset]などです。また、管理されたコードがあると、ゲームが「ハッキング可能」になります。いくつかのモッダーはIL注入を使って、私たちのAPIが許可しない場所にフックすることができます。これは良い面も悪い面もありますが、今のところ、モッダーが予想以上に進展できるようにしているので、プラスになっています。> 現代のUnityでは、パフォーマンスを達成したいなら、バーストコンパイラとHPC#を利用するアプローチが良い。そうですね、できればそれをしなくて済むといいんですけど。バーストとHPC#は複雑で、不必要な複雑さや人工的な制限を加えます。もしMonoと.NETの両方が同じくらい「遅い」なら、確かに高パフォーマンスを得るためにHPC#のトリックを使うべきですが、そうではないんです!現代の.NETは速いけど、Unityの開発者たちはそれを活用できないのがもどかしいです。ちなみに、最終的なトレースはC#のワーカースレッドとスレッドプールでした。> エディタのプロファイリングはいつも無駄な努力だ。そうかもしれないけど、私たち(開発者)は99%の時間をエディタで過ごしています。エディタからのパフォーマンス向上は、リリースビルドにも非常に似た割合で反映されることが多いです(一般的にはそうではないことも知ってますが、私の経験ではそうです)。以前に多くの重要な最適化を行ったことがあり、エディタからの測定はいつも有用な指標でした。あまり役に立たないのはUnityのプロファイラーで、特に「ディーププロファイル」を有効にすると、メソッドごとに一定のコストがかかり、小さなメソッドのコストを過大評価します。だから、私たちはそういうことをしない独自のトレースシステムを持っています。> このコメントセクションではGCについての言及が多いですが、プロのUnityプロジェクトは実行時にこれを最小限に抑えるために努力します。はい、アロケーションを最小限に抑えることは重要ですが、避けられないケースもたくさんあります。UIのための文字列処理のようなものは、毎フレーム大量のゴミを生成します。また、アロケーションフリーのオプションがないAPIもあります。CoreCLRはアロケーションをさらに削減し、より良いAPIを利用できるようにします。現在のGCが移動しないという事実だけでも、フラグメンテーションのためにメモリ消費が時間とともに増加します。「メモリ」リークの報告が多数あり、プレイヤーが定期的なロード/メニューへのクイットループの後にメモリ消費が増加することを報告しています。たとえ速いCoreCLR C#コード実行ができたとしても、これらの問題は残るので、改善されたGCが次のリストに入るでしょう。[1] https://steamdb.info/stats/releases/?tech=SDK.UnityIL2CPP
残念ながら、Unityのマーケティングに騙されちゃったみたいだね。状況をもう少しクリアにする必要があるよ。Unityのやり方は、ひどいものを作って、それを少しだけ改善するって感じ。実際には、これらのパフォーマンス向上策は、CoreCLRを使ってほとんどのコードを書くことや、真にパフォーマンスが重要な部分にC++を書くことに比べて、まだまだ足りてない。IL2Cppは生成されたコードのクソみたいなもので、.NET ILから低品質のC++コードを生成して、最適化コンパイラに decentなパフォーマンスを引き出させることに頼ってる。詳しくはここをチェックしてみて: https://unity.com/blog/engine-platform/il2cpp-internals-a-to... 結果的に、C#のあらゆる便利さ(コンパイル速度、使いやすさ、デバッグのしやすさ)を放棄して、現代の.NETのパフォーマンスにも及ばない。Burstコンパイラ/HPC#は、現代のゲーム開発文化が広めたすべてのミーム(構造体の配列、ECS)に乗っかってるけど、パフォーマンス的には、一般的に素朴に書かれたC++や時には.NET C#にも劣ることが多い。(ただし、正直に言うと、素朴なCoreCLR C#コードは、ハイパー最適化されたBurstの70-80%の速度くらいだよ)これらの技術は言うまでもなく、完全に独自のもので、コードを彼らのパラダイムに完全に合わせて設計しなきゃいけないし、Unityの外では使えない独自の非無料ライブラリを使わされるし、他にも厄介な副作用がある。この全体の詐欺的な販売手法は、常に(非常に遅い)ベースラインのMonoと比較してパフォーマンスを示すUnityのベンチマークによって成り立ってるけど、現代のC#やC++コンパイラとは比較してない。これらは確立された事実で、何度もベンチマークされてきたけど、Unityのマーケティングはどうにかして彼らの特別なコンパイラが技術的に優れているという話を広め続けてる。でも、真実が彼らに追いついてきて、彼らもCoreCLRを受け入れなきゃいけないことに気づいたみたいで、Unityに近々登場する予定だよ。人々がCoreCLRを使った通常のUnityコードが、彼らが3倍の時間をかけて書いたクソみたいなものと同じくらい速く動くことに気づくと、面白い会話になると思う。
Unityが2018年に始まってから.NET(CoreCLR)に移行できていないのが信じられない。そんなプロジェクトには1年か2年が限界だと思ってたのに。
CoreCLRはコンソールプラットフォームでは役に立たない。なぜならJITランタイムを出荷できないから。私の知識では、CoreCLRのAOTソリューションは、コンソール用のビルド出荷に必要なSDKやビルド要件のために機能しないと思う。いくつかのコンソールは、出荷されるネイティブコードがすべてSDKコンパイラでコンパイルされている必要があると要求します。たとえCoreCLRのAOTシステムをフォークしてコンソール用にビルドできるようにしても(そのコードはNDAのためオープンにはできない)、バイナリを出荷することは許可されません。IL2CPPが唯一の前進の道です。CoreCLRはPCでしか実行できません。
それは単なる誤解だよ。情報は10年前のもので、CoreCLR NativeAOTはすでにNintendoやPS5、Xboxで本物のゲームを出荷してる。JITはiPhoneでも使えないし、NativeAOTがその問題を解決してるんだ。それに、.NETは次のバージョンでWASMサポートをCoreCLRに移行する予定だよ(monoじゃなくて)。
そうだよ、カプコンはそれを使ってPS5のゲームを作ってるよ、例えばデビルメイクライとか。「RE:2023 C# 8.0 / .NETのゲームコードサポートと未来」 https://www.youtube.com/watch?v=tDUY90yIC7U いつも通り、GCの恐怖じゃなくて、ちゃんと技術があるかどうかの問題だね。
俺の夢は、adbでスマホにアクセスして、.Net SDKや.Net Runtime(v 8か10)をインストールして、アプリをAndroidでネイティブに動かせること。最初はシンプルなコンソールアプリから始めて、徐々に他のものも。Googleにはもうちょっとプラットフォームを開放してほしいな。adb経由でルートアクセスを有効にさせて、ポケットコンピュータの可能性を引き出させてほしい。ポータブルで低消費電力のサーバーを持ちたいし、自分の好きなスタックを動かしたい。すでに超効率的で、大容量ストレージもあるし、USB-Cハブをスマホに接続してストレージやLANネットワーク、キーボード/マウス、必要なら外部マイクも使える。WireGuardやTailscaleを使って軽量コンテナを動かすのも最高!でもUnityはほんとに追いつかないとね。.Net 8/10はマジで素晴らしくて速い。新しいガベージコレクターの変更で、ゲームのカクつきはほぼなくなるはず。関係ないけど、俺は基本的にSunlightとMoonlightを使ってメインのPCからスマホにコントローラーをつけてゲームをストリーミングしてるから、Diablo 2リマスターやHades、Grim Dawn、Xboxコントローラーサポートのあるゲームは全部スマホで直接プレイできる。めちゃくちゃうまくいってるよ。スマホは高dpiの画面と120hzのOLEDパネルを持ってるし、レンダリングはすべてヘッドレスのゲームサーバーで行われるから、最高設定でもバッテリーは全然減らない。何時間でもプレイできる。PlayストアからMonoベースのゲームをプレイするとバッテリーが2時間で切れちゃうし、Android向けに作られたゲームは大抵クソみたいに作られてるから、課金してもSteamにある同等のゲームよりひどいことが多い。
> 俺の夢は、adbでスマホにアクセスして、.Net SDKや.Net Runtime(v 8か10)をインストールして、アプリをAndroidでネイティブに動かせること。 それについては、MAUI(以前のXamarin)を使えば(ほぼ)できるって聞いたよ。 .Net SDKやRuntimeは手に入らないけど、Monoランタイムが使える。アプリにバンドルされてるから、実際には気づかないと思うよ。
ここにいる人たちの中で、Unityがなんで単に.NETで動かないのか知ってる人いる?モノのクロスプラットフォームの利点なんて、もう10年くらい前に消えちゃったよね。なんでUnityはil2cppみたいなハックに投資するんだろう?現代の.NETに移行すればいいのに。ライセンスの問題でもあるのかな?