.NET Core で高い CPU 使用率をデバッグする

この記事の対象: ✔️ .NET Core 3.1 SDK 以降のバージョン

このチュートリアルでは、過剰な CPU 使用率のシナリオをデバッグする方法について説明します。 示されている例の ASP.NET Core Web アプリ ソース コード リポジトリを使用して、デッドロックを意図的に発生させることができます。 エンドポイントは応答を停止し、スレッドが累積されます。 さまざまなツールを使用して、診断データのいくつかの重要な部分でこのシナリオを診断する方法について説明します。

このチュートリアルでは、次の作業を行います。

  • 高い CPU 使用率を調査する
  • dotnet-counters を使って CPU 使用率を確認する
  • dotnet-trace を使ってトレース生成を行う
  • PerfView でパフォーマンスをプロファイリングする
  • 過剰な CPU 使用率を診断して解決する

必須コンポーネント

このチュートリアルでは次のものを使用します。

CPU カウンター

診断データの収集を試行する前に、高い CPU 状態を確認する必要があります。 プロジェクトのルート ディレクトリから次のコマンドを使用して、サンプル アプリケーションを実行します。

dotnet run

このプロセス ID を検索するには、次のコマンドを使用します。

dotnet-trace ps

ご自分のコマンド出力からプロセス ID をメモしておきます。 プロセス ID は 22884 でしたが、この ID は異なります。 現在の CPU 使用率を確認するには、dotnet-counters ツール コマンドを使用します。

dotnet-counters monitor --refresh-interval 1 -p 22884

refresh-interval は、CPU 値をポーリングするカウンターの間隔を秒数で示します。 出力は次のようになります。

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                          0
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

Web アプリを実行している状態で、スタートアップ直後に CPU が使用されておらず、0% で報告されます。 ルート パラメーターとして 60000 を使用して api/diagscenario/highcpu ルートに移動します。

https://localhost:5001/api/diagscenario/highcpu/60000

次に、dotnet-counters コマンドを再実行します。 cpu-usage カウンターのみを監視する場合は、前のコマンドに '--counters System.Runtime[cpu-usage]' を追加します。 CPU が使用されているかどうかは不明であるため、上記と同じカウンターの一覧を監視して、カウンター値がアプリケーションに予期される範囲内であることを確認します。

dotnet-counters monitor -p 22884 --refresh-interval 1

次に示すように、CPU 使用率の増加が表示されます (ホスト マシンにより、さまざまな CPU 使用率が想定されます)。

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                         25
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

要求の期間中、CPU 使用率は上昇したパーセンテージで推移します。

ヒント

さらに高い CPU 使用率を視覚化するには、複数のブラウザー タブでこのエンドポイントを同時に実行します。

この時点で、CPU が予想以上に高くなっていることを確認できます。 問題の影響を特定することは、原因を見つけるための鍵となります。 診断ツールに加えて、高い CPU 使用率の影響を使って、問題の原因を見つけます。

プロファイラーで高い CPU 使用率を分析する

高い CPU 使用率のアプリを分析する場合は、コードの実行内容に関する分析情報を提供できる診断ツールが必要です。 通常はプロファイラーが適しており、さまざまなプロファイラー オプションを選択できます。 dotnet-trace はすべてのオペレーティング システムで使用できますが、セーフポイント バイアスとマネージドのみの呼び出し履歴の制限により、Linux の 'perf' や Windows の ETW などのカーネルを認識するプロファイラーと比較すると、より一般的な情報になります。 パフォーマンス調査がマネージド コードのみであれば、通常、dotnet-trace で十分です。

perf ツールを使用すると、.NET Core アプリ プロファイルを生成できます。 ここではこのツールを示しますが、dotnet-trace も同様に使用できます。 サンプル デバッグ ターゲットの前のインスタンスを終了します。

DOTNET_PerfMapEnabled 環境変数を設定して、.NET アプリによって /tmp ディレクトリ内に map ファイルが作成されるようにします。 この map ファイルは、CPU アドレスを名前順に JIT 生成関数にマップするために perf によって使用されます。 詳細については、「perf マップと jit ダンプをエクスポートする」を参照してください。

Note

.NET 6 では、.NET の実行時の動作を構成する環境変数のプレフィックスが、COMPlus_ ではなく DOTNET_ に標準化されています。 ただし、プレフィックス COMPlus_ は引き続き機能します。 以前のバージョンの .NET ランタイムを使用している場合は、環境変数に COMPlus_ プレフィックスをまだ使用する必要があります。

同じターミナル セッションで、サンプル デバッグ ターゲットを実行します。

export DOTNET_PerfMapEnabled=1
dotnet run

高 CPU API エンドポイント (https://localhost:5001/api/diagscenario/highcpu/60000) をもう一度実行します。 それが 1 分間の要求内で実行されている間に、プロセス ID を使用して perf コマンドを実行します。

sudo perf record -p 2266 -g

perf コマンドを実行すると、パフォーマンス コレクション プロセスが開始されます。 約 20 から 30 秒間実行してから、Ctrl + C キーを押してコレクション プロセスを終了します。 同じ perf コマンドを使用して、トレースの出力を確認できます。

sudo perf report -f

次のコマンドを使用して "フレーム グラフ" を生成することもできます。

git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

このコマンドを実行すると、flamegraph.svg が生成され、これをブラウザーに表示してパフォーマンスの問題を調査することができます。

Flame graph SVG image

Visual Studio で高い CPU 使用率データを分析する

すべての *.nettrace ファイルは、Visual Studio で分析することができます。 Linux の *.nettrace ファイルを Visual Studio で分析するには、*.nettrace ファイルとその他の必要なドキュメントを Windows マシンに転送し、*.nettrace ファイルを Visual Studio で開いてください。 詳細については、「CPU 使用率データを分析する」を参照してください。

関連項目

次の手順