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 • 注意事項 • ベンチマークとか取る際には気を付けよう • 手動での最適化は未だ大事