コード最適化とコンピューティング コスト削減の初級者向けガイド (C#、Visual Basic、C++、F#)
コンピューティング時間を短縮するとコストが削減されるため、コードを最適化するとお金を節約できます。 この記事では、さまざまなプロファイリング ツールを使用してこのタスクを実行する方法について説明します。
ここでは、手順を追って説明するのではなく、プロファイリング ツールを効果的に使用する方法とデータを解釈する方法を示します。 CPU 使用率ツールは、アプリケーション内でコンピューティング リソースが使われている場所をキャプチャして視覚化するのに役立ちます。 呼び出しツリーやフレーム グラフなどの CPU 使用率ビューでは、アプリ内で時間がかかっている箇所がわかりやすいグラフィックで示されます。 さらに、自動分析情報では、大きな影響を与える可能性のある正確な最適化が示される場合があります。 その他のプロファイリング ツールも、問題を特定するのに役立ちます。 ツールの比較については、「使用するパフォーマンス ツール」の記事をご覧ください。
調査を開始する
- 調査では、はじめに CPU 使用率トレースを取得します。 多くの場合、CPU 使用率ツールは、パフォーマンスの調査を開始し、コードを最適化してコストを削減するのに役立ちます。
- 問題の特定やパフォーマンスの向上に役立つ分析情報がさらに必要な場合は、他のプロファイル ツールのいずれかを使ってトレースを収集することを検討してください。 例:
- メモリの使用量を調べます。 .NET の場合は、最初に .NET オブジェクト割り当てツールを試してください。 .NET または C++ の場合は、メモリ使用量ツールを確認できます。
- アプリでファイル I/O を使っている場合は、ファイル I/O ツールを使います。
- ADO.NET または Entity Framework を使っている場合は、データベース ツールを使って SQL クエリ、正確なクエリ時間などを調べてみることができます。
データ コレクションの例
この記事で示すスクリーンショットの例は、ブログのデータベースに対してクエリを実行する .NET アプリと、関連するブログ記事に基づいています。 最初に CPU 使用率トレースを調べて、コードを最適化してコンピューティング コストを削減する余地がないかを探します。 起きていることがだいたい分かったら、他のプロファイル ツールからのトレースも確認して、問題を突き止めます。
データ コレクションには、次の手順が必要です (ここでは示していません)。
- アプリをリリース ビルドに設定します
- パフォーマンス プロファイラーから CPU 使用率ツールを選びます (Alt + F2 キー)。 (後の手順には、他にもいくつかのツールが含まれます。)
- パフォーマンス プロファイラーからアプリを開始してトレースを収集します。
CPU 使用率が高い部分を検査する
まず、CPU 使用率ツールを使ってトレースを収集します。 診断データを読み込んだら、まず、最上位の分析情報とホット パスが示されている最初の .diagsession レポート ページを調べます。 ホット パスは、アプリで CPU 使用率が最も高いコード パスを示します。 これらのセクションでは、改善できるパフォーマンスの問題をすばやく特定するのに役立つヒントが提供される場合があります。
呼び出しツリー ビューでホット パスを見ることもできます。 このビューを開くには、レポートの [詳細を開く] リンクを使って、[呼び出しツリー] を選びます。
このビューでもホット パスがわかります。アプリの GetBlogTitleX
メソッドで、アプリの CPU 使用率の約 60% という高い CPU 使用率が示されています。 ただし、GetBlogTitleX
の セルフ CPU 値は低く、約 10% しかありません。 合計 CPU とは異なり、 セルフ CPU 値は他の関数で費やされた時間を除外するため、実際のボトルネックについては、呼び出しツリー ビューの下を調べることができます。
非常に高い セルフ CPU 値によって証明されるように、GetBlogTitleX
では 2 つの LINQ DLL への外部呼び出しが行われ、CPU 時間の大部分を使用しています。 これは、最適化する部分として LINQ クエリを調べる最初の手掛かりです。
視覚化された呼び出しツリーと、データの別のビューを見るには、フレーム グラフ ビューに切り替えます (呼び出しツリーと同じ一覧から選びます)。 ここでも、アプリの CPU 使用率の多くは GetBlogTitleX
メソッドによるものであるようです (黄色で表示)。 GetBlogTitleX
ボックスの下に示されている LINQ DLL の外部呼び出しで、メソッドのすべての CPU 時間が使われています。
追加データを収集する
多くの場合、他のツールは、分析に役立つ追加情報を提供し、問題を特定できます。 たとえば、LINQ DLL を特定したので、まずデータベース ツールを試します。 このツールと 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"
ここでは、必要以上に多くの列値を取得していることに注意してください。
アプリのメモリ使用量を調べるには、.NET オブジェクト割り当てツールを使ってトレースを収集します (C++ の場合は、代わりにメモリ使用量ツールを使用します)。 メモリ トレースの [呼び出しツリー] ビューにはホット パスが示され、メモリ使用量が多い部分を突き止めるのに役立ちます。 もう驚くことではありませんが、GetBlogTitleX
メソッドで多くのオブジェクトが生成されているように見えます。 実際、900,000 を超えるオブジェクトが割り当てられています。
作成されるオブジェクトのほとんどは、文字列、オブジェクト配列、Int32 です。 ソース コードを調べることで、これらの型がどのように生成されているかわかる場合があります。
コードの最適化
ここで、GetBlogTitleX
のソース コードを見てみましょう。 .NET オブジェクト割り当てツールで、メソッドを右クリックして、[ソース ファイルに移動] を選びます。 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 クエリを最適化し、このコードを考え出す方法に関するいくつかの一般的な推奨事項を見つけます。
foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
Console.WriteLine("Post: " + x);
}
このコードでは、クエリの最適化に役立ついくつかの変更を行いました。
Where
句を追加し、foreach
ループの 1 つを削除します。- この例で必要なのは、ステートメント内の
Select
Title プロパティだけです。
次に、プロファイル ツールを使って再テストします。
結果を確認する
コードを更新した後、CPU 使用率ツールをもう一度実行してトレースを収集します。 呼び出しツリー ビューでは、GetBlogTitleX
がアプリの CPU 合計の 37% である 1754 ミリ秒しか実行していないことが示され、59% から大幅に改善されています。
フレーム グラフ ビューに切り替えて、改善を別の視覚化で確認します。 このビューでも、GetBlogTitleX
で使われる CPU が少なくなっています。
データベース ツール トレースで結果を調べると、このクエリを使うと読み取られるレコードの数が 100,000 ではなく 2 つのみになっています。 また、クエリは大幅に簡略化され、以前は生成されていた不要な LEFT JOIN がなくなっています。
次に、.NET オブジェクト割り当てツールの結果をもう一度確認すると、GetBlogTitleX
によるオブジェクトの割り当ては 56,000 のみであり、900,000 から 95% 近く減っていることがわかります。
繰り返す
複数の最適化が必要な場合があり、コードの変更を繰り返し続けて、パフォーマンスを向上させ、コンピューティング コストを削減する変更を確認できます。
次のステップ
次のブログ投稿では、Visual Studio のパフォーマンス ツールを効果的に使用する方法を学習するのに役立つ情報を提供します。
関連するコンテンツ
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示