.NET ラボ 2023/10/28 での発表資料
ブログ https://blog.neno.dev/entry/2023/10/29/131336
.NET 8 で既定で有効になったDynamic PGO について.NET ラボ 2023/10/28何縫ねの。
View Slide
自己紹介1• 所属: NTTコミュニケーションズイノベーションセンター• 趣味: C#, OSS, ドール, 一眼(α7 IV)• 執心領域• C# ⇔ TypeScript• SignalR何縫ねの。nenoNaninunenoMakeブログ https://blog.neno.devその他 https://neno.dev
OSS 紹介2属性を付与するだけTapper• C# の型定義から TypeScript の型定義を生成する .NET Tool/ library• JSON / MessagePack 対応!https://github.com/nenoNaninu/Tapper
OSS 紹介3• C# の SignalR Client を強く型付けするための Source GeneratorTypedSignalR.ClientBeforeAfter (using TypedSignalR.Client)こんな SignalR のHub と Receiver の interface があったとして…脱文字列!全てが強く型付け!https://github.com/nenoNaninu/TypedSignalR.Client
4• TypeScript の SignalR Client を強く型付けするための .NET Tool / libraryTypedSignalR.Client.TypeScriptBeforeAfter (using TypedSignalR.Client.TypeScript)脱文字列!全てが強く型付け!TypeScript 用の型をC# から自動生成MessagePack Hub Protocol 対応!https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript属性を付与するだけ!OSS 紹介
5• SignalR 使ったアプリを快適に開発するための GUI を自動生成する library• 2 step で利用可能!• http pipeline に middleware の追加• Hub と Receiver を定義してるinterface に属性を付与• JWT 認証 サポート• パラメータのユーザ定義型サポート• JSON で入力!SignalR 版 SwaggerUITypedSignalR.Client.DevToolshttps://github.com/nenoNaninu/TypedSignalR.Client.DevToolsOSS 紹介
.NET 8 リリース直前6
.NET 8 リリース直前7毎 年 恒 例https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/
.NET 8 リリース直前8Σ(゚Д゚)https://github.com/dotnet/runtime/pull/86225※ Tiered PGO = Dynamic PGO
お品書き9• 歴史と概観• Instrumentation• 最適化例• 注意事項
歴史と概観10
歴史と概観11• .NET Core 2.1• Tiered compilation が導入。ただし既定では無効。• .NET Core 3.0• Tiered compilation が既定で有効• .NET 6• Dynamic PGO 導入。ただし既定では無効。• .NET 7• On-Stack Replacement (OSR) が導入• .NET 8• Dynamic PGO が 既定で有効歴史
歴史と概観12Tiered compilation とは?w/o Tiered compilationILJIThttps://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.mdOptimized code
歴史と概観13Tiered compilation とは?w/o Tiered compilationILJITOptimized codex86assembly 等https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.md
歴史と概観14Tiered compilation とは?w/o Tiered compilation w/ Tiered compilationILJITILJITQuick JITTier0Unoptimized codeTire1Optimized codeOptimized codex86assembly 等https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.md
歴史と概観15Tiered compilation とは?コンパイル速度優先最適化甘めw/o Tiered compilation w/ Tiered compilationILJITILJITQuick JITTier0Unoptimized codeTire1Optimized codeOptimized codex86assembly 等https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.md
歴史と概観16Tiered compilation とは?コンパイル速度優先最適化甘めw/o Tiered compilation w/ Tiered compilationILJITILJITQuick JITTier0Unoptimized codeTire1Optimized code.NET 8 では最低限度の最適化はされているOptimized codex86assembly 等https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.md
歴史と概観17Tiered compilation とは?コンパイル速度優先最適化甘めw/o Tiered compilation w/ Tiered compilationILJITILJITQuick JITTier0Unoptimized codeTire1Optimized code高頻度で呼ばれるメソッドは最適化.NET 8 では最低限度の最適化はされているOptimized codex86assembly 等https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/tiered-compilation.md
歴史と概観18• 実行中のメソッド (= On-Stack) の code をあるバージョンから別のバージョンに切り替える(Replacement)の技術。• e.g., Tire0 code から Tire1 code に切り替える等。• Backward branch (≒ループ) を含むメソッドで特に嬉しい。• .NET 6 以前はループを含んでいたら Tire1 直行だった。• OSRが実装されてない場合、無限ループが永遠に Tire0 で実行されてしまうため。• .NET 7 以後はループを含んでいても Tire0 で実行された後、ループ回数が多い場合にのみ Tire1 で実行可能になった。On-Stack Replacement (OSR) とは?https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/OnStackReplacement.mdDynamic PGO 的に重要(無限ループをプロファイリング結果をもとに最適化可能)
歴史と概観19• PGO : Profile-guided optimization• その名の通り、プロファイリング結果をもとに最適化する。• Static PGO1. アプリケーションコードに計測用のコードを挿入し2. 主要なシナリオで実行 & 計測し3. その計測結果をもとに最適化をかけてリビルド• Dynamic PGO• Static PGO がやっていた事を全部実行時に行う• JIT の強みDynamic PGO とは?https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/DynamicPgo.mdTiering と OSR が導入されたおかげでここまで出来るように
歴史と概観20• RedyToRun (R2R) を使うかどうかで違いが出てくる。Dynamic PGO の流れhttps://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/
歴史と概観21• 前提• Tier0 では可能な限り素早く JIT を終わらせたい。• 一方で最適化は時間がかかる。• 幾つかの最適化は上記を両立しながら出来る。• JIT が Tier0 で消費する時間の多くは runtime の VM とのやりとり• 型の解決など• 不要な分岐を大幅に削除できれば Tier 0 でのコンパイルを高速化しながら、code の品質も向上。Tier 0 で行われる最適化
歴史と概観22• 定数畳み込み• 定数式は (実行時ではなく) コンパイル時に評価• JIT intrinsic• If(typeof(T).IsValueType) の分岐が消失する等Tier 0 で行われる最適化
歴史と概観23• 定数畳み込み• 定数式は (実行時ではなく) コンパイル時に評価• JIT intrinsic• If(typeof(T).IsValueType) の分岐が消失する等Tier 0 で行われる最適化 定数畳み込みT が int じゃなかったら消し飛ぶ(.NET 7以前では Tier1 の最適化)
歴史と概観24だそうです。VM って結局なに。https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/
Instrumentation25
Instrumentation26• Dynamic PGO のためには各メソッドを何回呼んだかなど計測をする必要がある。• 計測する用のコードを実行時に挿し込む。• メソッドはマルチスレッドで同時に呼ばれる可能性がある。計測の必要性
Instrumentation27• Dynamic PGO のためには各メソッドを何回呼んだかなど計測をする必要がある。• 計測する用のコードを実行時に挿し込む。• メソッドはマルチスレッドで同時に呼ばれる可能性がある。計測の必要性どうやってスレッドセーフに、正確に、効率的に計測するか?
Instrumentation281. 素朴に count++ (Racy)• .NET 7 以前はこうだった。• マルチスレッドだと一部のインクリメントが無効になるが、欲しいのは近似なので問題がないと思われていた。• 結構な誤差が出て間違った最適化がかかってしまう等の問題が出てきていた。2. Interlocked.Increment• 正確なカウントは実現できた• ただしコストが非常に重たかった。3. Scalable Approximate Counting• 賢いカウント方法を導入。呼び出し回数の計測方法
Scalable Approximate Counting29https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting30log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting31log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting32log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…2進数リテラルhttps://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting33log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…2進数リテラル𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting34log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…2進数リテラル𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…いい感じに mask になるhttps://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting35log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…32bit の rand と delta-1 の論理積を取ると…2進数リテラル𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…いい感じに mask になるhttps://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting36log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…32bit の rand と delta-1 の論理積を取ると…初回 ½ で true (1bit の mask)この時 delta = 0b10 = 2いい感じに mask になる2進数リテラルhttps://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting37log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…32bit の rand と delta-1 の論理積を取ると…初回 ½ で true (1bit の mask)この時 delta = 0b10 = 2いい感じに mask になる2進数リテラル次に ¼ で true (2bit の mask)この時 delta = 0b100 = 4https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting38log𝟐𝒄𝒐𝒖𝒏𝒕 (小数点以下切り捨て)log𝟐𝒄𝒐𝒖𝒏𝒕 ≥ 𝟏𝟑𝒄𝒐𝒖𝒏𝒕 ≥ 𝟐𝟏𝟑 = 𝟖𝟏𝟗𝟐𝒅𝒆𝒍𝒕𝒂 ቐ𝟎𝒃𝟏𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟎𝟎 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…𝒅𝒆𝒍𝒕𝒂 − 𝟏 ቐ𝟎𝒃𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟑)𝟎𝒃𝟏𝟏 (log𝟐𝒄𝒐𝒖𝒏𝒕 = 𝟏𝟒)…32bit の rand と delta-1 の論理積を取ると…初回 ½ で true (1bit の mask)この時 delta = 0b10 = 2いい感じに mask になる2進数リテラル次に ¼ で true (2bit の mask)この時 delta = 0b100 = 4最終的に delta が加算https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting39• X 軸が最終的な試行回数、Y 軸が精度• CPU のスレッド数は 12精度は?https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting40• X 軸が最終的な試行回数、Y 軸が精度• CPU のスレッド数は 12精度は?Scalable が優秀https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting41• X 軸が最終的な試行回数、Y 軸が精度• CPU のスレッド数は 12精度は?Scalable が優秀一方で Racy は許容できない誤差https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting42• Racy を取り除いた図。• Scalable はワーストケースでも 3% のズレで済んでいる。精度は?https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting43• X 軸が最終的な試行回数、Y 軸がかかった合計時間• 217 を超えたあたりから Interlocked が顕著に高コストに。速度は?https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Scalable Approximate Counting44• X 軸が最終的な試行回数、Y 軸がかかった合計時間• 217 を超えたあたりから Interlocked が顕著に高コストに。速度は?Scalable と Racy はほぼ変らない性能https://github.com/dotnet/runtime/blob/v8.0.0-rc.2.23479.6/docs/design/features/ScalableApproximateCounting.md
Instrumentation45• Dynamic PGO では devirtualization 等のためにvirtual method / interface method の呼び出し先の具象型が何で、それが何回実行されたか…とかが知りたい。• 最もよく呼ばれる型は devirtualization する。• しかし、呼び出された具象型を正確にカウントすることは高価。• なお、PGO 的に知りたいのは割合。正確な回数でない。呼び出し回数以外の counting
Instrumentation46• Dynamic PGO では devirtualization 等のためにvirtual method / interface method の呼び出し先の具象型が何で、それが何回実行されたか…とかが知りたい。• 最もよく呼ばれる型は devirtualization する。• しかし、呼び出された具象型を正確にカウントすることは高価。• なお、PGO 的に知りたいのは割合。正確な回数でない。呼び出し回数以外の countingさてどうするか 🤔
Instrumentation47• Sampling とは• n 個ある母集団から k 個取り出す操作• n が既知かつそこそこのサイズまでは簡単• Dynamic PGO で求められる sampling はちょっと難しい• 未知の n に対応• 母集団が呼び出しの集合なので、当然 n は未知であり、増加し続ける• 省メモリかつ高速• 全ての呼び出しを保存とかは当然不適切。• つまり、過去の呼び出しを遡るとかもできない。• 一様なサンプリングSampling して解決。ただし…
Instrumentation48• Sampling とは• n 個ある母集団から k 個取り出す操作• n が既知かつそこそこのサイズまでは簡単• Dynamic PGO で求められる sampling はちょっと難しい• 未知の n に対応• 母集団が呼び出しの集合なので、当然 n は未知であり、増加し続ける• 省メモリかつ高速• 全ての呼び出しを保存とかは当然不適切。• つまり、過去の呼び出しを遡るとかもできない。• 一様なサンプリングSampling して解決。ただし…要するに終端の分からないストリームから一様にサンプリングしないといけない
Instrumentation49• Reservoir : 貯水池• サンプリング結果が reservoir に保存される• ストリームを1方向に舐めながら、一様にサンプリング可能な素敵アルゴリズム• これが一様サンプリングである証明はいろいろ文献あるのでそちらを参照のこと• なお、こまやかな調整が必要だった模様。• https://github.com/dotnet/runtime/pull/87332Reservoir sampling
Instrumentation50• Reservoir : 貯水池• サンプリング結果が reservoir に保存される• ストリームを1方向に舐めながら、一様にサンプリング可能な素敵アルゴリズム• これが一様サンプリングである証明はいろいろ文献あるのでそちらを参照のこと• なお、こまやかな調整が必要だった模様。• https://github.com/dotnet/runtime/pull/87332Reservoir sampling
Instrumentation51• 静的な解析に基づく最適化、いろいろありますよね。• 分岐予測とか。• Dynamic PGO は静的な profile と instrumentation によって得られたprofile を合成して最適化を行う。• profile synthesis とかいう。Dynamic PGO による最適化はinstrumentation にのみ基づくわけではないhttps://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/
最適化例52
最適化例53• Devirtualization• Inliningで、実際どういう最適化されるの?他にもいろいろあるけど大きな柱はこの 2 つ
最適化例54Devirtualization + Inlining の具体例
最適化例55Devirtualization + Inlining の具体例
最適化例56どんな最適化が?
最適化例57どんな最適化が?
最適化例58どんな最適化が?PGO が無ければ普通に interface の dispatch.Dynamic PGO により devirtualization
最適化例59どんな最適化が?PGO が無ければ普通に interface の dispatch.Dynamic PGO により devirtualizationGuardeddevirtualization(GDV)と呼ばれる最適化
最適化例60どんな最適化が?PGO が無ければ普通に interface の dispatch.Dynamic PGO により devirtualizationGuardeddevirtualization(GDV)と呼ばれる最適化
最適化例61どんな最適化が?PGO が無ければ普通に interface の dispatch.Dynamic PGO により devirtualizationさらに Inline 化Guardeddevirtualization(GDV)と呼ばれる最適化
最適化例62どんな最適化が?PGO が無ければ普通に interface の dispatch.Dynamic PGO により devirtualizationさらに Inline 化Guardeddevirtualization(GDV)と呼ばれる最適化
最適化例63x86 assembly を覗いてみると….NET 7 .NET 8
最適化例64x86 assembly を覗いてみると….NET 7 .NET 8型のチェックが挟まっている
最適化例65x86 assembly を覗いてみると….NET 7 .NET 8型のチェックが挟まっている42 が埋め込まれている(0x2A は 42)
最適化例66• 既定では 1 つ。• DOTNET_JitGuardedDevirtualizationMaxTypeChecks 環境変数で設定可能。Guarded devirtualization される型は幾つまで?
最適化例67• 既定では 1 つ。• DOTNET_JitGuardedDevirtualizationMaxTypeChecks 環境変数で設定可能。Guarded devirtualization される型は幾つまで?必ずしもパフォーマンスが改善するとは限らないので注意(悪化する場合も)
最適化例68• 既定では 1 つ。• DOTNET_JitGuardedDevirtualizationMaxTypeChecks 環境変数で設定可能。Guarded devirtualization される型は幾つまで?Generic host 全盛の昨今では1 interface 1 class で DI に登録されるので既定で十分高速化が期待できる必ずしもパフォーマンスが改善するとは限らないので注意(悪化する場合も)
最適化例69Delegate にも Guarded devirtualization + inlining
最適化例70Delegate にも Guarded devirtualization + inlining常に _func が呼び出されているだけ
最適化例71Delegate にも Guarded devirtualization + inlining常に _func が呼び出されているだけ毎回 delegate の invoke は無駄が大きい
最適化例72• GDV + inlining により既知の delegate ならdelegate の invoke を避けるようなコードを生成Delegate にも Guarded devirtualization + inlining疑似コード
最適化例73• GDV + inlining により既知の delegate ならdelegate の invoke を避けるようなコードを生成Delegate にも Guarded devirtualization + inlining疑似コードDelegate の invoke は避けられた
最適化例74• GDV + inlining により既知の delegate ならdelegate の invoke を避けるようなコードを生成Delegate にも Guarded devirtualization + inlining疑似コードDelegate の invoke は避けられたループの中で何度も比較は無駄…
最適化例75• Hoisting という最適化を行う• ループの中で不変なのであれば、ループの外に出すDelegate にも Guarded devirtualization + inlining + α疑似コード最適化対象の delegate かの比較はループの外に出せた
最適化例76• Hoisting という最適化を行う• ループの中で不変なのであれば、ループの外に出すDelegate にも Guarded devirtualization + inlining + α疑似コード最適化対象の delegate かの比較はループの外に出せたループの中で分岐がまだある…
最適化例77• Loop cloning という最適化を行うDelegate にも Guarded devirtualization + inlining + α疑似コード
最適化例78• Loop cloning という最適化を行うDelegate にも Guarded devirtualization + inlining + α疑似コード最終的に大幅な高速化
注意事項79
注意事項80• マイクロベンチマークなどのいくつかのパターンでは、Dynamic PGO を強く意識する必要がある。• Dynamic PGO on/off して双方でベンチマークとるなどの対策が必要。• なぜ?• マイクロベンチマークで Dynamic PGO が有効になってしまうと、開発者が書いたコードが原因で高速化が実現できたのか、Dynamic PGO によって高速化が実現できたのか、分からなくなってしまう。高速化の原因は Dynamic PGO かも?
注意事項81• 開発者が書く際に手動で最適化をしなくて良くなる訳ではない。• 例えば具象型でいいフィールドを interface にしない、とか。手動の最適化はいまだ有効.NET 8 からこれを促すanalyzer が入るそうです
まとめ82• 歴史と概観• Tiered compilation• On-Stack Replacement• Dynamic PGO• Tier 0 での最適化• Instrumentation• Scalable Approximate Counting• Reservoir sampling• 最適化例• Guarded Devirtualization• Inlining• 注意事項• ベンチマークとか取る際には気を付けよう• 手動での最適化は未だ大事