プログラミングとソフトウェア開発

JITから非同期プログラミングまで、.NET 11の改善が性能向上を積み重ねる

Microsoftは.NET 11で数百件の改善を紹介しており、JITコンパイラー、割り当ての削減、インターフェイスやデリゲートの呼び出し、async/awaitの内部構造に明確な重点を置いています。公開された結果によれば、向上の要因は単一の大きな変更ではなく、不要なチェック、割り当て、呼び出しを取り除いたことにあります。

2026-09-15
1 分で読めます
7 閲覧数
فريق تحرير certi.news
JITから非同期プログラミングまで、.NET 11の改善が性能向上を積み重ねる

Microsoftは、2026年9月15日付で.NET Blogに公開した記事で、.NET 11に導入された数百件の改善を紹介し、それらをJIT(実行時)コンパイラー、ランタイム、ライブラリに及ぶ長期的な取り組みの累積的な成果として説明しています。基本的な考え方は、性能を倍増させる単独の機能が存在するというものではなく、不要になった境界チェック、廃止されたメモリー割り当て、回避されたロックやシステムコール、より少ないプロセッサーサイクルで実行されるようになったループなど、小さな改善を繰り返し取り除くことです。

この種の改善が重要なのは、その大部分がアプリケーションコードの変更や再設計を必要としない点です。JITコンパイラーは、通常C#、F#、Visual Basicから生成される中間言語ILを、プロセッサーが実行するネイティブ命令へ変換します。仮想呼び出しを特定の型へディスパッチできることや、あるチェックが失敗しないことを証明できれば、より短い命令を生成し、関数を相互にインライン化できる可能性を高められます。

抽象化の除去の改善

記事の大きな部分では、Microsoftが「抽象化の除去」と呼ぶもの、つまり開発者がプログラム設計で必要とする抽象化の一部について、ランタイムが実行コストを乗り越えられるようにする取り組みに焦点を当てています。例としてインターフェイス呼び出しや仮想関数があり、従来の実行では複数のポインターを読み込み、間接呼び出しを行う必要があるため、呼び出された関数を現在の関数にインライン化できない場合があります。

.NET 11では、「Guarded Devirtualization(ガード付き非仮想化)」と呼ばれる手法の開発が続けられています。JITは実行時に最も一般的な型を認識し、それを直接呼び出す高速パスを生成しながら、後から別の型が現れた場合に実行の正しさを保証するため、仮想パスも維持します。これによって関数をインライン化できる場合、定数の伝播、分岐の除去、境界チェックの除去など、ほかの最適化も可能になります。

この取り組みは、ジェネリック仮想関数、ReadyToRunおよびNativeAOTの操作、インターフェイスにおけるデフォルト実装にも及びます。記事によれば、直接呼び出しによってインライン化が増えると、これらの改善によってコードサイズが大きくなる可能性がありますが、プログラムのロジックがオプティマイザーにとってより見えやすくなります。

割り当ての削減とガベージコレクターへの負荷軽減

.NET 11では、Escape Analysis(エスケープ解析)の拡張も続けられています。これは、関数内で作成されたオブジェクトがその関数の範囲を超えるかどうかを判定しようとするものです。その範囲から出ないことが証明されれば、JITはそれをマネージドヒープに置くことを回避でき、一部のケースでは割り当て自体を完全に排除できるため、ガベージコレクターへの負荷が軽減されます。

記事では、Nullable boxingの改善や、コレクション列挙における条件付きエスケープ解析の例を示しています。ある測定では、インスタンスフィールドから構築されたコレクションの列挙において、32バイトの割り当てが消失し、実行時間が.NET 10の13.874ナノ秒から.NET 11の2.674ナノ秒に短縮されました。また、ジェネリック値やインターフェイス呼び出しを使用する一部のケースでは、24バイトの一時的な割り当てを回避できるようになりました。

これらの数値は非常に小さな操作に関するものなので、すべてのアプリケーションで一般的かつ一定の性能向上が得られるものとして読むべきではありません。主な意義は、ランタイムが最適化できるコードパターンを明らかにすることにあります。実際の影響は、アプリケーションの性質とホットパスに左右されます。

デリゲートと非同期ランタイム

.NET 11にはCoreCLRにおけるデリゲート表現の変更も含まれています。その一つとして、64ビットプロセスでは各デリゲートオブジェクトからポインター1個分のサイズのフィールドを削除し、記事によればデリゲート1個あたり8バイトを節約します。NativeAOTにおけるデリゲート表現のフィールドも再配置され、Arm64などのアーキテクチャーでアクセスを改善するため、共に使用される一部の値がメモリー上で近くに配置されました。

記事では、「runtime async」という名前の新しい構造も紹介しています。これは、async/awaitメソッドの変換に関する責任の一部をC#コンパイラーからJITとランタイムへ移します。従来のモデルでは、コンパイラーが、パラメーター、ローカル値、awaiter、実行状態など、再開に必要なフィールドを含むステートマシンを生成します。一方、新しいモデルではIL内のより小さな契約に依存し、サスペンドポイントで何が生存しているか、継続オブジェクトをどのように配置するか、外部から見えるTaskまたはValueTaskをどのように生成するかに関する判断をランタイムとJITに委ねます。

実際には何が変わるのか?

今回の取り組みから読み取れる編集上の要点は、.NET 11が新しいAPIの追加と同じ程度、既存コードの改善にも力を入れているということです。インターフェイス、ジェネリック関数、デリゲート、列挙、非同期処理に大きく依存するアプリケーションは、ソースコードを直接変更せずに恩恵を受けられる可能性があります。ただし、改善の程度はプロセッサー、オペレーティングシステム、ランタイム設定、負荷の性質によって異なります。

Microsoftは、BenchmarkDotNetを使用して結果をテストし、.NET 10と.NET 11を固定したうえで、同じコードを両方のバージョンで実行することを推奨しています。また、公開された測定は非常に小さなテストであり、ハードウェア、ほかのプロセス、環境設定の影響を受ける可能性があると注意しています。そのため、小規模なベンチマークの結果だけでは、アップグレードを決定したり、全面的な改善を証明したりするには不十分であり、より広範な結論を採用する前に、アプリケーションの実際の負荷を測定する必要があります。

ニュースの出典
ف
著者

فريق تحرير certi.news

同じカテゴリー

おすすめ記事

すべてのニュースを見る