次の方法で共有


ケース スタディ: パフォーマンスの問題を切り分ける (C#、Visual Basic、F#)

プロファイリング ツールを使用して、パフォーマンスの問題を調査し、問題のある箇所を特定します。 このケース スタディでは、パフォーマンスの問題があるサンプル アプリケーションを使用して、効率を向上させるプロファイリング ツールの使用方法を示します。 プロファイリング ツールを比較する場合は、「使用するツール」を参照してください。

このケース スタディでは、次のトピックについて説明します。

  • Visual Studio プロファイリング ツールを使用して、アプリケーションのパフォーマンスを分析する方法。
  • これらのツールによって提供されるデータを解釈して、パフォーマンスのボトルネックを特定する方法。
  • .NET カウンター、呼び出し回数、タイミング データに重点を置いて、コードを最適化するための実践的な戦略を適用する方法。

さらにこれらの手法を独自のアプリケーションに適用して、効率とコスト効率を高めます。

パフォーマンスの問題の特定に関するケース スタディ

このケース スタディのサンプル アプリケーションは、シミュレートされたデータベースに対してクエリを実行する ASP.NET アプリです。 この例は、診断の例に基づいています。

サンプル アプリケーションの主なパフォーマンスの問題は、非効率的なコーディング パターンにあります。 このアプリケーションには、効率に大きく影響するパフォーマンスのボトルネックがあります。 この問題には次の症状が含まれます。

  • CPU 使用率が低い: アプリケーションの CPU 使用率が低いため、CPU がボトルネックではないことが示されています。

  • ThreadPool スレッド数が多い: スレッド数が比較的多く、着実に増加していることから、スレッド プールの枯渇が示唆されています。

  • アプリケーションの応答が遅い: 使用可能なスレッドが不足しているため、新しい作業項目を処理できず、アプリケーションの応答が遅くなっています。

このケース スタディでは、Visual Studio のプロファイリング ツールを使用してアプリケーションのパフォーマンスを分析することで、これらの問題に対処することを目的としています。 アプリケーションのパフォーマンスを向上させることができる箇所と方法を理解することで、開発者はコードをより高速かつ効率的にするための最適化を実装できます。 最終的な目標は、アプリケーションの全体的なパフォーマンスを向上させ、実行の効率とコスト効率を高めることです。

課題

サンプル .NET アプリケーションのパフォーマンスの問題に対処するには、いくつかの課題があります。 これらの課題は、パフォーマンスのボトルネックの診断の複雑さから生じます。 説明した問題を解決するための主な課題は次のとおりです。

  • パフォーマンスのボトルネックの診断: 主な課題の 1 つは、パフォーマンスの問題の根本原因を正確に特定することです。 CPU 使用率が低いにもかかわらずパフォーマンスが低い場合は、複数の要因が考えられます。 開発者はプロファイリング ツールを効果的に使用してこれらの問題を診断する必要がありますが、そのためにはこれらのツールの動作方法とその出力の解釈方法をある程度理解している必要があります。

  • 知識とリソースの制約: チームは知識、専門知識、リソースに関連する制約に直面する可能性があります。 アプリケーションのプロファイリングと最適化には特定のスキルと経験が必要であり、すべてのチームがこれらのリソースにすぐにアクセスできるとは限りません。

これらの課題に対処するには、プロファイリング ツールの効果的な使用、技術的な知識、慎重な計画とテストを組み合わせた戦略的なアプローチが必要です。 このケーススタディの目的は、開発者にこのプロセスをガイドし、これらの課題を克服してアプリケーションのパフォーマンスを向上させるための戦略と洞察を提供することです。

戦略

このケース スタディのアプローチの概要を次に示します。

  • 調査では、まずパフォーマンス データを収集しながら、.NET カウンター メトリックを監視します。 CPU 使用率ツールと同様、Visual Studio の .NET カウンター ツールも、パフォーマンス調査を始めるにあたって推奨されるツールです。
  • 次に、問題の特定やパフォーマンスの向上に役立つ追加の分析情報を得るために、他のいずれかのプロファイリング ツールを使用したトレースの収集を検討します。 たとえば、インストルメンテーション ツールを使用して、呼び出し回数とタイミング データを確認します。

データ コレクションには次のタスクが必要です。

  • アプリをリリース ビルドに設定する。
  • パフォーマンス プロファイラーから .NET カウンター ツールを選びます (Alt + F2 キー)。 (後の手順には、インストルメンテーション ツールが含まれます。)
  • パフォーマンス プロファイラーからアプリを開始してトレースを収集します。

パフォーマンス カウンターをチェックする

アプリの実行中に、.NET Counters ツールでカウンターを観察します。 最初の調査のため、注意が必要な主要指標を次にいくつか示します。

  • CPU Usage= このカウンターを見ると、CPU 使用率が高いか低いかでパフォーマンスの問題の有無を確認できます。 これは、特定の種類のパフォーマンスの問題の手がかりになる可能性があります。 例:
    • CPU 使用率が高い場合は、CPU 使用率ツールを使用して、コードを最適化できる可能性のある箇所を特定します。 このトピックに関するチュートリアルについては、「ケース スタディ: コードの最適化に関する初級者向けガイド」を参照してください。
    • CPU 使用率が低い場合は、インストルメンテーション ツールを使用して、ウォール クロック時間に基づく平均の関数実行時間および呼び出し数を確認します。 これは、競合やスレッド プールの枯渇などの問題の特定に役立つ場合があります。
  • Allocation Rate= 要求を処理する Web アプリの場合、このレートは概ね安定している必要があります。
  • GC Heap Size= このカウンターを見ると、メモリ使用量が継続的に増加し、リークする可能性の有無を確認できます。 メモリ使用率が高いようであれば、いずれかのメモリ使用率ツールを使用します。
  • Threadpool Thread Count= 要求を処理する Web アプリの場合、このカウンターを見ると、スレッド数が安定しているか、安定して増加しているかどうかを確認できます。

次に示すのは、CPU Usage が低く、ThreadPool Thread Count が比較的高い場合の例です。

.NET カウンター ツールで示されているカウンターのスクリーンショット。

CPU 使用率が低く、スレッド数が安定して増加している場合、スレッド プールが枯渇している可能性があります。 スレッド プールでは、強制的に新しいスレッドが回転し続けます。 スレッド プールの枯渇は、新しい作業項目の処理に使用できるスレッドがプールにない場合に発生し、多くの場合、アプリケーションの応答が遅くなります。

CPU 使用率が低く、スレッド数が比較的多い場合、スレッド プールが枯渇する可能性があるケースの理論に従って、インストルメンテーション ツールの使用に切り替えます。

呼び出し数とタイミング データを調査する

インストルメンテーション ツールのトレースを見て、スレッドで何が起こっているかをより詳しく調べてみましょう。

インストルメンテーション ツールでトレースを収集し、それを Visual Studio に読み込んだ後、まず集約したデータを表示する最初の .diagsession レポート ページを確認します。 収集されたトレースで、レポート内の [詳細を開く] リンクをクリックした後、[フレーム グラフ] を選択します。

インストルメンテーション ツールのフレーム グラフのスクリーンショット。

視覚化されたフレーム グラフにより、QueryCustomerDB 関数 (黄色で表示) がアプリの実行時間の大部分を占めていることがわかります。

QueryCustomerDB 関数を右クリックし、[呼び出しツリーで表示] を選択します。

インストルメンテーション ツールの呼び出しツリーのスクリーンショット。

アプリで CPU 使用量が高いコード パスは、ホット パスと呼ばれます。 ホット パスの炎アイコン (ホット パス アイコンを示すスクリーンショット。) は、改善される可能性があるパフォーマンスの問題をすばやく特定するのに役立ちます。

コール ツリー ビューでは、ホット パスに QueryCustomerDB 関数が含まれていることがわかります。これは、パフォーマンスの問題が考えられることを示しています。

他の関数で費やされた時間に比べて、QueryCustomerDB 関数の Self 値および Avg Self 値が非常高いです。 TotalAvg Total とは異なり、Self 値では他の関数で費やされた時間が除外されるため、パフォーマンスのボトルネックを探すのに適しています。

ヒント

Self 値が高くなく、比較的低い場合は、QueryCustomerDB 関数によって呼び出された実際のクエリを調べたいと思うでしょう。

QueryCustomerDB 関数をダブルクリックして、関数のソース コードを表示します。

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

少し調べてみます。 時間を節約して Copilot に調査を任せることもできます。

Copilot を使用している場合は、コンテキスト メニューから [Copilot に質問する] を選択して次の質問を入力します。

Can you identify a performance issue in the QueryCustomerDB method?

ヒント

/optimize などのスラッシュ コマンドを使用して、Copilot に対する適切な質問を作成できます。

Copilot は、このコードが await を使用せずに非同期 API を呼び出していることを示します。 これは ThreadPool の枯渇における最も一般的な原因である sync-over-async コード パターンです。これにより、スレッドがブロックされる可能性もあります。

解決するには、await を使用します。 この例では、Copilot は次のコードの提案と説明を提供します。

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

データベース クエリに関連するパフォーマンスの問題が発生した場合は、データベース ツールを使用して、特定の呼び出しが遅いかどうかを調査できます。 このデータにより、クエリを最適化する機会が示される場合があります。 データベース ツールを使用してパフォーマンスの問題を調査する方法を説明するチュートリアルについては、「ケース スタディ: コードの最適化に関する初級者向けガイド」を参照してください。 データベース ツールは、ADO.NET または Entity Framework Core を使用した .NET Core をサポートします。

Visual Studio で個々のスレッドの動作の視覚化するには、デバッグ中に [並列スタック] ウィンドウを使用できます。 このウィンドウには、個々のスレッドと、待機中のスレッド、待機する際の目的のスレッド、デッドロック状態に関する情報が表示されます。

スレッド プールの枯渇についての詳細は、「ThreadPool の枯渇の検出」を参照してください。

次のステップ

次の記事とブログ投稿では、Visual Studio パフォーマンス ツールの効果的な使用方法を学習するのに役立つ情報を提供しています。