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