アプリケーションのギアを上げよう
― Visual Studio 2010でアプリケーションのパフォーマンス・チューニング ―

第2回 Visual Studioのプロファイラを使って性能評価を行う

亀川 和史
2011/05/31
Page1 Page2

マルチスレッド・アプリケーションの競合状態を収集する

 マルチスレッド・アプリケーションは、デバッグも性能評価もしづらいソフトウェアだ。CPUコアがすべて100%の使用状態であっても性能をフルに引き出しているとは限らない。そのようなマルチスレッド・アプリケーションの実行状況を、Visual Studioのプロファイラは視覚化して表示してくれる。

マルチスレッド・アプリケーションをプロファイルする場合の注意点

 その場合のプロファイラ実行の手順そのものは、前述のプロファイル実行と変わらない。ただし、注意点が3つある。

(1)[管理者として実行]したVisual Studioから実行しなくてはならない。

(2)[パフォーマンス ウィザード](ページ 1/3)の[同時実行]ラジオボタンを選択すると有効になる[マルチスレッド アプリケーションの動作を視覚化]チェックボックスにチェックした場合、性能測定対象のアプリケーションが起動する直前に次の画面のようなメッセージボックスが表示されるので、[はい]ボタンを選択すると、[オプション]ダイアログが表示され、そこで[シンボル ファイル (.pdb) の場所:]欄の[Microsoft シンボル サーバー]チェックボックスにチェックを入れる。このように設定してMicrosoftシンボル・サーバにアクセスできるようにしておけば、より詳細な情報の表示が可能になる。なお、一度ダウンロードしたシンボル・ファイルは、キャッシュ・フォルダに格納されるので、ある程度のディスク容量が必要になる。

Microsoftシンボル・サーバを有効にするためのメッセージボックス

(3)「64bit版では、完全なスタック・トレースを採取できない」という旨の警告が表示される。完全なスタック・トレースが必要な場合は、「HKEY_LOCAL_MACHINE\CurrentControlSet\Control\Session Manager\Memory Management」キーの中に、REG_DOWRD型の「DisablePagingExecutive」という値を作り、「1」を設定し、最後にOSの再起動を行う。これに関する説明は、「KB184419:NT Executive ページングからディスクを停止する方法」で解説されている、明示的にプロジェクトを「x86」に設定して、実行可能ファイルを生成、実行すればよい。

パフォーマンス・ウィザードの実行(同時実行)

 [同時実行]のプロファイル・レポートは、前述の[CPU サンプリング]のプロファイルと同様の手順で行う。

 具体的には、メニューバーの[分析]から[パフォーマンス ウィザードの起動]を選択する。CPUサンプリングのときと同じ[パフォーマンス ウィザード]ダイアログが表示される。

 今回は、次の画面のように[同時実行]を選択してマルチスレッドの性能測定を行う。マルチスレッド・アプリケーションの場合、リソースの競合が速度低下の重要な要因になるため、[リソース競合データを収集]チェックボックスにチェックを付ける。また、注意点として挙げたとおり、レポートを見やすくするために、[マルチスレッド アプリケーションの動作を視覚化]チェックボックスにもチェックを付ける。

マルチスレッド・アプリケーションの性能測定を行うための[パフォーマンス ウィザード]ダイアログの設定

 後は、CPUプロファイリングのときと同様にウィザードを進めればよい。もしも[管理者として実行]していないVisual Studioであれば、以下の警告メッセージが表示されて、Visual Studioの再起動を促される。

Visual Studioの再起動を促す警告メッセージ

 この警告メッセージで[異なる資格情報で再起動]を選ぶと、Visual Studioが再起動し、特権昇格の後、自動的にソリューションも読み込まれる。後は、再度、同じ手順でパフォーマンス・ウィザードを起動すればよい。

 プロファイラからアプリケーションが起動されると、CPUサンプリングと同様の手順で、アプリケーション性能測定を行いたいマルチスレッドの処理を実施する。アプリケーションを終了すると、Visual Studioが同時実行プロファイルのレポートを作成する。

 レポート作成はかなり負担のかかる処理であるため、マシン・スペックが高くない場合や、回線速度によってはシンボル情報をダウンロードする場合、相当時間が必要となるので注意してほしい。

「同時実行」プロファイル・レポートの参照

 レポートが作成されると、Visual Studio内に以下のような画面が表示される。CPUサンプリング実行とはかなり異なる画面だ。

「同時実行」プロファイル・レポートの表示
レポート・ファイルを開くと、このような性能に関する各種情報を閲覧できる。
  ビューの切り替えを行う。「競合」「コア」「スレッド」といった、マルチスレッド・プロファイル固有のビューが追加される。
  同時実行の視覚化を行っているビュー(具体的には「CPU 使用状況」「スレッド」「コア」)に切り替えるボタン。これらのビューはでも選択できる。
  競合度合いのグラフ。
  競合の合計を多い順に並べたリソースのグラフ。
  競合の合計を多い順に並べたスレッドのグラフ。

「同時実行」プロファイル・レポートの参照:CPU使用状況

 まず、[CPU 使用状況]ボタンをクリックすると、CPUの使用状況にグラフが切り替わる。CPUの使用状況によって配色が異なっている。

「同時実行」プロファイル・レポートの「CPU 使用状況」ビュー
CPUの使用状況によって配色が異なっている。
  ズームして領域を拡大/縮小する。
  黄色の部分はプロファイル対象外のプロセスを表している。
  システム・プロセス。つまりOSカーネル(kernel)が使用している時間。
  灰色の部分はアイドル・プロセス(つまりCPUが使われていない状態)。
  緑色の部分はプロファイル対象プログラム(今回の例では「Raytracer」)のCPU使用状況。

 [ズーム]スライダーを左右に動かすと、細かいところも見やすく拡大/縮小できる。拡大すると、定期的に3コア以上がアイドル状態になっていることが分かる。つまり、このプログラムは何らかの待ちが発生している状態であるだろうことが、このレポート表示から分かる。

「同時実行」プロファイル・レポートの「CPU 使用状況」ビューを拡大ズームしたところ

「同時実行」プロファイル・レポートの参照:スレッド/コア

 再び、画面上にある[スレッド]ボタンや[コア]ボタンを押すと、「.NET FrameworkのCLRワーカー・スレッド」/「ワーカー・スレッド」単位での表示、コア単位での表示に切り替わる。なお、このプロファイルを採取したマシンは、4コアでハイパースレッドありなので、OS上では「論理8コア」に見えている。

 少し長くなるが、「スレッド」ビュー全体を表示する。

「同時実行」プロファイル・レポートの「スレッド」ビュー
「.NET FrameworkのCLRワーカー・スレッド」/「ワーカー・スレッド」単位で表示されている。
  プロファイル中に生成されたCLRワーカー・スレッド/ワーカー・スレッドの一覧および、(この画面の例では表示されていないが)上へスクロールすると物理ディスクに対するアクセス状況も表示される。
  スレッドごとの使用状況を視覚的に表した状況。色が意味するところについては、左下のの凡例で解説されている。
  プロファイル上で「どの色がどの状態を意味するか」を説明している。
  の特定の領域をクリックすると、そのタイミングのスレッドのスタック情報および、スレッドをブロックしている別スレッドがあればそのスレッドに関する情報に表示を切り替える。

 ここでも、画面上にあるスライダーで表示を少し拡大してみよう。

「同時実行」プロファイル・レポートの「スレッド」ビューを拡大ズームしたところ

 赤枠の箇所では長い間(約95ミリ秒)、メモリ管理が実行されている。つまりガベージ・コレクションが発生しているのではないかと考えられる。この赤枠で囲まれている[メモリ管理]の水色のところをマウスでクリックすると、画面下に[現在のスタック]タブが開かれて、該当箇所のスタック状態が表示され、何が起きているのかを知ることができる。次の画面はその例である。

「同時実行」プロファイル・レポートの「スレッド」ビュー内の[現在のスタック]タブ

 上の画面の[現在のスタック]タブの内容を見ると、CLRのgc_heapでメモリを確保しようとしているが、Page Faultが発生し、95ミリ秒の間、待ちになっている。そして、スレッドをブロックしているスレッドは、すぐ下にある「CLR ワーカー スレッド(11632)」ということが分かる。

 隣のタブである[ブロック解除スタック]をクリックすると、以下のようにブロック解除のコールスタックが表示される。

「同時実行」プロファイル・レポートの「スレッド」ビュー内の[ブロック解除スタック]タブ

 上の画面の[ブロック解除スタック]タブの内容を見ると、赤枠部分がソース・コード上の該当箇所において、直接の原因となった処理になる。これをダブルクリックすれば、次の画面のようにソース・コードを開いて該当箇所へジャンプする。

直接の原因となった処理となる該当箇所のソース・コード

 上のコードの「ISect」はクラスとして定義されているので、「恐らくGC(ガベージ・コレクション)が発生して、スレッド間の待ち合わせが発生したのではないか?」と推測できる。それを確認するには、パフォーマンス・ウィザードで[.NET メモリ割り当て]を調査する。

パフォーマンス・ウィザードの実行(.NETメモリ割り当て)

.NETにおけるメモリ割り当てを調査するための[パフォーマンス ウィザード]ダイアログの設定

 これまでと同様にウィザードを実行すると、次の画面のようにプロファイル結果がVisual Studioに表示される。メモリ割り当てのプロファイリングはかなり重い処理だが、今回のプログラムの場合、最初の1〜2秒で結果が分かるので、すぐにプロファイルを中止すれば問題ない。

「.NET メモリ割り当て」プロファイル・レポートの表示

 上の画面の[最も多くのメモリを割り当てられた型]を見ると、ISectクラスが実に97%も消費している。よって、「このクラスのメモリ割り当ての削減を検討するべき」という課題が簡単に判明する。

 このISectクラスは構造体に書き換えられる。構造体に書き換えて、再度、プロファイルを採取してみると、次の画面のようにメモリ管理で95ミリ秒も消費しているところは見えなくなった([メモリ管理]の水色と似た、少し濃い水色は[スリープ])。

 もちろんまだまだチューニングできる余地はある。

「.NET メモリ割り当て」プロファイル・レポートにより判明した課題に対処した場合の、「同時実行」プロファイル・レポートの「スレッド」ビュー

 画面上の[コア]をクリックして、論理CPUコアへの割り当て状況を可視化すると、書き換えた状態で以下のようになっている。

「同時実行」プロファイル・レポートの「コア」ビュー

 これを見ると、コンテキスト・スイッチが各スレッドあたり大きいところで10%あり、CLRワーカー・スレッドがいくつものCPUコアに移動していることが分かる。また、コアによってはかなり長い間作業していない状態もある。例えば「論理コア 0」と「論理コア 4」および「論理コア 5」の作業量の違いはかなり大きいことが、このグラフを見るだけでも分かる。

 これは恐らく、データ分割範囲が適正ではないため、コアあたりの作業量に差が出ているのだろう。このあたりから先はトライ&エラーで調整していく必要がある。ソース・コードを見て試してみてほしい。

 このようにして、一見、マルチコアを使い切っているようなアプリケーションでも、実は使い切っていない場合がある。Visual Studioのプロファイラでスレッド実行状態を可視化することにより、リソース競合やGCによる速度低下の箇所を発見しやすくなる。

【コラム】スタンドアロンのプロファイラについて

 性能測定は、限りなく本番環境で行うことが望ましいが、本番環境にVisual Studioをインストールできない場合も多いだろう。そのような場合のために、スタンドアロンのプロファイラが提供されている。

 最小構成でインストールされるため、可能であればまずはスタンドアロンのプロファイラを検討してみてほしい。このプロファイラ自体は、Microsoftダウンロード・センターから入手可能だが、採取した情報を分析するにはPremiumエディション以上のVisual Studio 2010が必要になる。

Visual StudioのProfessional以下のエディションで使う場合

 本稿で紹介したプロファイラ機能はPremium以上のエディションでのみ使用可能だが、Professionalエディション以下ではどうすればいいだろうか? その場合は、前回紹介したWindows Performance Analyzerを使用すればよい

 次回は、WPF/Silverlightのパフォーマンスや、データベース・アクセスのパフォーマンスについて取り上げる。end of article


 INDEX
  アプリケーションのギアを上げよう ― Visual Studio 2010でアプリケーションのパフォーマンス・チューニング
  第2回 Visual Studioのプロファイラを使って性能評価を行う
    1.プロファイラで実行情報を採取する
  2.マルチスレッド・アプリケーションの競合状態を収集する

インデックス・ページヘ 「アプリケーションのギアを上げよう」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH