コンピューティング時間を短縮するとコストが削減されるため、コードを最適化することでコストを削減できます。 このケース スタディでは、パフォーマンスの問題があるサンプル アプリケーションを使用して、プロファイリング ツールを使用して効率を向上させる方法を示します。 プロファイリング ツールを比較する場合は、「どのツールを選択すればよいか」を参照してください。
このケース スタディでは、次のトピックについて説明します。
- コードの最適化の重要性と、コンピューティング コストの削減への影響。
- Visual Studio プロファイリング ツールを使用してアプリケーションのパフォーマンスを分析する方法。
- これらのツールによって提供されるデータを解釈して、パフォーマンスのボトルネックを特定する方法。
- CPU 使用率、メモリ割り当て、データベース操作に重点を置いて、コードを最適化するための実用的な戦略を適用する方法。
次に、これらの手法を独自のアプリケーションに適用して、効率とコスト効率を高めます。
最適化のケース スタディ
このケース スタディで調べたサンプル アプリケーションは、ブログとブログ投稿のデータベースに対してクエリを実行する .NET アプリケーションです。 .NET の一般的な ORM (Object-Relational マッピング) である Entity Framework を使用して、SQLite ローカル データベースと対話します。 アプリケーションは多数のクエリを実行するように構成されており、広範なデータ取得タスクを処理するために .NET アプリケーションが必要になる可能性がある実際のシナリオをシミュレートします。 サンプル アプリケーションは、Entity Framework の概要サンプルの変更されたバージョンです。
サンプル アプリケーションの主なパフォーマンスの問題は、コンピューティング リソースを管理し、データベースと対話する方法にあります。 アプリケーションには、効率に大きな影響を与えるパフォーマンスのボトルネックがあり、その結果、アプリケーションの実行に関連するコンピューティング コストが発生します。 この問題には、次の現象が含まれます。
CPU 使用率が高い: アプリケーションは、大量の CPU リソースを不必要に消費する方法で非効率的な計算や処理タスクを実行する可能性があります。 これにより、応答時間が遅くなり、運用コストが増加する可能性があります。
非効率的なメモリ割り当て: アプリケーションでは、メモリの使用と割り当てに関連する問題が発生することがあります。 .NET アプリでは、非効率的なメモリ管理によってガベージ コレクションが増加し、アプリケーションのパフォーマンスに影響を与える可能性があります。
データベース操作のオーバーヘッド: データベースに対して多数のクエリを実行するアプリケーションでは、データベースの操作に関連するボトルネックが発生する可能性があります。 これには、非効率的なクエリ、過剰なデータベース呼び出し、Entity Framework 機能の不十分な使用が含まれます。これらはすべてパフォーマンスを低下させる可能性があります。
このケース スタディでは、Visual Studio のプロファイリング ツールを使用してアプリケーションのパフォーマンスを分析することで、これらの問題に対処することを目的としています。 アプリケーションのパフォーマンスをどこでどのように改善できるかを理解することで、開発者は最適化を実装して CPU 使用率を削減し、メモリ割り当ての効率を向上させ、データベースの相互作用を合理化し、リソース使用率を最適化できます。 最終的な目標は、アプリケーションの全体的なパフォーマンスを向上させ、実行の効率とコスト効率を高めることです。
課題
サンプルの .NET アプリケーションでパフォーマンスの問題に対処するには、いくつかの課題があります。 これらの課題は、パフォーマンスのボトルネックの診断の複雑さに起因します。 説明されている問題を解決する際の主な課題は次のとおりです。
パフォーマンスのボトルネックの診断: 主な課題の 1 つは、パフォーマンスの問題の根本原因を正確に特定することです。 高い CPU 使用率、非効率的なメモリ割り当て、およびデータベース操作のオーバーヘッドには、複数の要因が発生する可能性があります。 開発者は、プロファイリング ツールを効果的に使用してこれらの問題を診断する必要があります。これには、これらのツールのしくみと出力の解釈方法を理解する必要があります。
知識とリソースの制約: 最後に、チームは知識、専門知識、リソースに関連する制約に直面する可能性があります。 アプリケーションのプロファイリングと最適化には特定のスキルと経験が必要であり、すべてのチームがこれらのリソースにすぐにアクセスできるわけではありません。
これらの課題に対処するには、プロファイリング ツールの効果的な使用、技術的な知識、慎重な計画とテストを組み合わせた戦略的アプローチが必要です。 このケース スタディは、開発者がこのプロセスをガイドし、これらの課題を克服し、アプリケーションのパフォーマンスを向上させるための戦略と分析情報を提供することを目的としています。
戦略
このケース スタディのアプローチの概要を次に示します。
- CPU 使用率トレースを取得して調査を開始します。 Visual Studio の CPU 使用率ツール は、パフォーマンスの調査を開始し、コードを最適化してコストを削減するのに役立ちます。
- 次に、問題の特定やパフォーマンスの向上に役立つ追加の分析情報を取得するために、他のプロファイリング ツールのいずれかを使用してトレースを収集します。 例えば:
- メモリ使用量について見てみましょう。 .NET の場合は、最初に .NET オブジェクト割り当てツール 試します。 (.NET または C++ の場合は、代わりにメモリ使用量ツールを確認できます)。
- ADO.NET または Entity Framework では、Database ツール を使用して、SQL クエリ、正確なクエリ時間などを調べることができます。
データ収集には、次のタスクが必要です。
- アプリをリリース ビルドに設定する。
- パフォーマンス プロファイラー (alt + F2) から CPU 使用率ツールを選択します。 (後の手順には、他のいくつかのツールが含まれます)。
- パフォーマンス プロファイラーからアプリを起動し、トレースを収集します。
CPU 使用率が高い領域を検査する
CPU 使用率ツールを使ってトレースを収集し、Visual Studio に読み込んだ後、概要データを示す .diagsession レポートページを最初に確認します。 レポートの [詳細を開く] リンクを使用します。
レポートの詳細ビューで、呼び出しツリー ビューを開きます。 アプリの CPU 使用率が最も高いコード パスは、ホット パスと呼ばれます。 ホットパスフレームアイコン (ホットパスアイコンを示す) は、改善の見込みのあるパフォーマンス問題をすばやく特定するのに役立ちます。
呼び出しツリー ビューでは、アプリ内の GetBlogTitleX
メソッドがアプリの CPU 使用率の約 60% を占めているため、CPU 使用率が高いことがわかります。 しかし、GetBlogTitleX
の セルフCPU 値は低く、約 0.10%です。 合計 CPUとは異なり、セルフ CPU 値は他の関数で費やされた時間を除外するため、呼び出しツリーの下で実際のボトルネックを調べることができます。
GetBlogTitleX
は、2 つの LINQ DLL に対して外部呼び出しを行います。これは CPU 時間の大部分を使用しています。これは、非常に高い セルフ CPU 値によって証明されます。 これは、LINQ クエリが最適化する領域になる可能性がある最初の手掛かりです。
視覚化された呼び出しツリーとデータの別のビューを取得するには、Flame Graph ビューを開きます。 (または、GetBlogTitleX
を右クリックし、[Flame Graph 表示] を選択します)。ここでも、GetBlogTitleX
メソッドがアプリの CPU 使用率の多くを担当しているようです (黄色で表示)。 LINQ DLL の外部呼び出しが GetBlogTitleX
ボックスの下に表示され、メソッドのすべての CPU 時間を使用しています。
追加データを収集する
多くの場合、他のツールは、分析に役立つ追加情報を提供し、問題を特定できます。 このケース スタディでは、次のアプローチを取ります。
- まず、メモリ使用量を確認します。 CPU 使用率が高い場合とメモリ使用量が多い場合は、相関関係がある可能性があるため、両方を確認して問題を特定すると役立ちます。
- LINQ DLL を特定したため、データベース ツールについても説明します。
メモリ使用量を確認する
メモリ使用量の観点からアプリで何が起こっているかを確認するには、.NET オブジェクト割り当てツールを使用してトレースを収集します (C++ の場合は、代わりにメモリ使用量ツールを使用できます)。 メモリ トレースの 呼び出しツリー ビューにはホット パスが表示され、メモリ使用量が多い領域を特定するのに役立ちます。 この時点で、GetBlogTitleX
メソッドは多くのオブジェクトを生成しているように見えます。 実際には、900,000 を超えるオブジェクト割り当て。
作成されるオブジェクトのほとんどは、文字列、オブジェクト配列、および Int32s です。 ソース コードを調べることで、これらの型がどのように生成されるかを確認できる場合があります。
データベース ツールでクエリを確認する
パフォーマンス プロファイラーでは、CPU 使用率ではなくデータベース ツールを選択します (または、両方を選択します)。 トレースを収集したら、診断ページの クエリ タブを開きます。 データベース トレースの [クエリ] タブで、最初の行に最長のクエリ (2446 ミリ秒) が表示されます。 レコード 列には、クエリが読み取るレコードの数が表示されます。 この情報は、後で比較するために使用できます。
LINQ によって生成された SELECT
ステートメントをクエリ列で調べることで、最初の行を GetBlogTitleX
メソッドに関連付けられたクエリとして識別します。 完全なクエリ文字列を表示するには、列の幅を展開します。 完全なクエリ文字列は次のとおりです。
SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"
アプリでは、必要以上に多くの列値がここで取得されていることに注意してください。 ソース コードを見てみましょう。
コードを最適化する
GetBlogTitleX
ソース コードを見てみましょう。 データベース ツールで、クエリを右クリックし、[ソース ファイルに移動] 選択します。 GetBlogTitleX
のソース コードでは、LINQ を使用してデータベースを読み取る次のコードを見つけます。
foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
{
foreach (var post in blog.Posts)
{
if (post.Author == "Fred Smith")
{
Console.WriteLine($"Post: {post.Title}");
}
}
}
このコードでは、foreach
ループを使用して、作成者として "Fred Smith" を含むブログをデータベースで検索します。 これを見ると、多数のオブジェクトがメモリ内で生成されていることがわかります。データベース内の各ブログの新しいオブジェクト配列、各 URL の関連付けられた文字列、投稿に含まれるプロパティの値 (ブログ ID など)。
少し調査を行い、LINQ クエリを最適化する方法に関するいくつかの一般的な推奨事項を見つけます。 あるいは、時間を節約して Copilot に調査を任せることもできます。
Copilot を使用している場合は、コンテキスト メニュー [Copilot に質問する] を選択し、次の質問を入力します。
Can you make the LINQ query in this method faster?
ヒント
/optimize などのスラッシュ コマンドを使用して、Copilot に関する適切な質問を作成できます。
この例では、Copilot は、次の推奨されるコード変更と説明を提供します。
public void GetBlogTitleX()
{
var posts = db.Posts
.Where(post => post.Author == "Fred Smith")
.Select(post => post.Title)
.ToList();
foreach (var postTitle in posts)
{
Console.WriteLine($"Post: {postTitle}");
}
}
このコードには、クエリの最適化に役立ついくつかの変更が含まれています。
Where
句を追加し、foreach
ループの 1 つを削除しました。- この例で必要となる
Select
ステートメントの Title プロパティだけを示しました。
次に、プロファイリング ツールを使用して再テストします。
結果
コードを更新した後、CPU 使用率ツールを再実行してトレースを収集します。 呼び出しツリーの ビューでは、アプリの CPU 合計の 37% を使用して、GetBlogTitleX
が 1754 ミリ秒しか実行されていないことが示されています。これは、59%からの大幅な改善です。
Flame Graph ビューに切り替えて、改善点を示す別の視覚化を表示します。 このビューでは、GetBlogTitleX
は CPU の小さな部分も使用します。
データベース ツール トレースの結果を確認します。100,000 ではなく、このクエリを使用して読み取られたレコードは 2 つだけです。 また、クエリは大幅に簡略化され、以前に生成された不要な LEFT JOIN が不要になります。
次に、.NET オブジェクト割り当てツールの結果を再確認し、GetBlogTitleX
が 56,000 個のオブジェクト割り当てのみを担当し、900,000 から約 95% 削減されることを確認します。
繰り返す
複数の最適化が必要な場合があり、コードの変更を反復処理し続けて、パフォーマンスを向上させ、コンピューティング コストを削減するのに役立つ変更を確認できます。
次の手順
次の記事とブログ記事では、Visual Studio パフォーマンス ツールを効果的に使用する方法を学習するのに役立つ詳細情報を提供します。
- ケース スタディ: パフォーマンスの問題 を分離する
- ケース スタディ: 30 分以内で 2 倍のパフォーマンス
- 新しいインストルメンテーション ツール を使用して Visual Studio のパフォーマンスを向上させる
関連コンテンツ
- どのツールを使用する必要がありますか?
- パフォーマンス プロファイラー で CPU 使用率を分析する
- メモリ分析ツールの選択
- データベースパフォーマンス を分析する
- プロファイリングでのはじめに