インテル® Parallel Studio− 入門ガイド ー

エクセルソフト株式会社
www.xlsoft.com

Rev. 1.1 (2010/04/08)

1 / 48

―

目次−

１．はじめに ............................................................................................................................................................................................................................. 3
２．インテル® Parallel Studio 概要............................................................................................................................................................................. 3
３．基本使用方法................................................................................................................................................................................................................... 4
３−１．基本開発工程................................................................................................................................................................................................... 5
３−２．使用サンプルコード................................................................................................................................................................................... 6
３−３．サンプルコードのビルド（インテル® Parallel Composer 使用）................................................................................ 8
３−４．ホットスポットの検出（インテル® Parallel Amplifier 使用） .................................................................................... 12
３−５．マルチスレッドの実装（インテル® Parallel Composer 使用）................................................................................... 16
３−６．マルチスレッドコードのデバッグ（インテル® Parallel Composer 使用） ........................................................ 19
３−７．マルチスレッドコードのデバッグ（インテル® Parallel Inspector 使用） ......................................................... 23
３−８．マルチスレッドコードの修正（インテル® Parallel Composer 使用）................................................................... 27
３−９．チューニング（インテル® Parallel Amplifier 使用）.......................................................................................................... 30
４．関連情報 .......................................................................................................................................................................................................................... 36
４−１．インテル® Parallel Composer ............................................................................................................................................................... 36
４−１−１．主要コンパイルオプション................................................................................................................................................ 36
４−２．インテル® Parallel Inspector................................................................................................................................................................. 40
４−２−１．メモリーチェック機能........................................................................................................................................................... 40
４−２−２．メモリーチェック機能に推奨されるコンパイルオプション...................................................................... 41
４−３．インテル® Parallel Amplifier ................................................................................................................................................................. 42
４−３−１．推奨されるコンパイルオプション ................................................................................................................................ 42
４−３−２．特定箇所のプロファイル方法........................................................................................................................................... 43
５．追加情報 .......................................................................................................................................................................................................................... 46
５−１．ドキュメントの参照方法 ..................................................................................................................................................................... 46
５−２．N-Queens サンプルコード.................................................................................................................................................................. 47
５−３．OpenMP について..................................................................................................................................................................................... 47
５−４．TBB について .............................................................................................................................................................................................. 47
６．最後に................................................................................................................................................................................................................................ 48

2 / 48

１．はじめに本ドキュメントでは、インテル® Parallel Studio の基本的な使用方法について説明します。説明には製品とともにインストールされるサンプルを使用して、アプリケーションの並列化を行います。本ドキュメントを通して、インテル® Parallel Studio に含まれるツールの基本機能および並列化の作業手順を習得することができます。また関連・追加情報として、各ツールの補足内容や並列化に関する情報を記載しています。なお、本ドキュ
メントは、インテル® Parallel Studio 初期バージョンをベースに記載されています。

２．インテル® Parallel Studio 概要
インテル® Parallel Studio は、Microsoft* Visual Studio* に統合され、C/C++ 言語の開発者向けにアプリケーショ
ンの並列化を支援するツールであり、以下の 3 つのツールで構成されています。
zインテル® Parallel Composer¾

アプリケーションのパフォーマンスを引き出すインテル® コンパイラーをはじめ、マルチスレッド対
応高速ライブラリーを含むビルドツールです。インテル® Parallel Composer には以下のコンポーネン
トが含まれています。
１）インテル® C++ コンパイラー （IA-32 および Intel® 64 対応アプリケーション向け）
２）インテル® インテグレーテッド・パフォーマンス・プリミティブ (インテル® IPP)
３）インテル® スレッディング・ビルディング・ブロック (インテル® TBB)
４）インテル® Parallel Debugger Extension（OpenMP 専用デバッガー）z

インテル® Parallel Inspector¾

プログラム内の「メモリーエラー」チェックおよび「スレッドエラー」チェックを行うデバッグツールです。「スレッドエラー」チェックでは、スレッド間における共有データ・アクセス・チェック等を行い、Win 32 スレッド API、OpenMP、インテル® TBB のプログラミング手法をサポートします。

zインテル® Parallel Amplifier¾

パフォーマンス低下の原因となるボトルネックの調査やアプリケーションの並列動作の状態を分析するチューニング・ツールです。

3 / 48

３．基本使用方法
本章では、インテル® Parallel Studio の基本的な使用方法について説明します。
まず Visual Studio を起動してみましょう。下図のように新しいツールバーが追加されていることを確認してください。

「インテル® Parallel Amplifier 用ツールバー」「インテル® Parallel Inspector 用ツールバー」「インテル® Parallel Debugger Extension 用ツールバー（インテル® Parallel Composer）」「インテル® C++ コンパイラー用ツールバー（インテル® Parallel Composer）」

本製品（インテル® Parallel Studio）には、以下の 3 つのツールが含まれています。
•

インテル® Parallel Composer•インテル® Parallel Amplifier•

インテル® Parallel Inspector

これらのツールはすべてマルチスレッド・プログラミングを支援するためのツールであり、それぞれのツールの役割を理解して、マルチスレッド開発工程における位置づけを認識することが大切です。本章では各ツールの使用方法を説明する前に、まず本製品を使用してマルチスレッド・プログラミングを行う基本的な開発工程（ワークフロー）を説明します。その後、サンプルコードを使用して、このワークフローに沿った手順でそれぞれのツールの使い方を説明していきます。
なお、本章で使用するシステムの構成は以下のとおりです。

プロセッサー：Intel(R) Core(TM)2 Quad CPU Q6600 2.4GHz
OS：Microsoft Windows Vista* Business X86
IDE：Microsoft Visual Studio 2008 Team System（以下、VS2008）

4 / 48

３−１．基本開発工程
ここでは、本製品を使用してプログラムの並列化を行う基本開発工程（ワークフロー）について説明します。
（１）

インテル® Parallel Amplifier で現在のシリアル・アプリケーションのホットスポットを検出します。
ホットスポットとは、コード内で CPU 負荷の最も高い処理を意味します。

（２）

検出されたホットスポットに対してマルチスレッドによる並列処理を実装します。

（３）インテル® Parallel Composer を使用して並列化されたアプリケーションをコンパイルします。

（４）インテル® Parallel Composer 付属の Parallel Debugger Extension で、マルチスレッドのデバッグを行
います。

（５）

インテル® Parallel Inspector で、マルチスレッド共有データおよびメモリーチェックを行います。

（５’） エラーが存在する場合は、コードを修正して再コンパイルを行います。
（６）

再度インテル® Parallel Amplifier で、実装したマルチスレッドを分析してチューニングを行います。

（６’） チューニングの余地がある場合は、コードを修正し再コンパイルを行います。
（７）

さらに最適化可能なホットスポットの検証を行います。

インテル® Parallel Amplifier（１）ホットスポットの検出

インテル® Parallel Composer（２）マルチスレッドの実装（３）コンパイル（４）デバッグ（５’）コードの修正
インテル® Parallel Inspector（５）共有データチェック（７）さらなる最適化

メモリーチェック

インテル® Parallel Amplifier（６）チューニング

5 / 48（６’）チューニングの実施

３−２．使用サンプルコード
本章で使用するサンプルコードは、インテル® Parallel Composer に付属する N-Queens サンプル・プロジェク
トの１つです。本製品をデフォルト設定でインストールした場合は、この N-Queens サンプル・プロジェクトは以下の Zip ファイルとしてインストールされます。
C:¥Program Files¥Intel¥Parallel Studio¥Composer¥Samples¥en_US¥C++ ¥NQueens.zip
この Zip ファイルを解凍すると、複数のプロジェクトファイルが生成されます。本章で使用するサンプルコー
ドは、 nq-serial プロジェクトに含まれる以下のファイルです。
¥NQueens¥nq-serial¥nq-serial.cpp
この N-Queens プロジェクトは、ある特定のマス数のチェスボードにおいて、Queen の駒を安全に配置できるパターンの総数を求める問題です。チェスのルールで Queen は縦、横、斜め（将棋の飛車と角行を合わせた動き）に動けるので、この動作範囲を考慮して Queen 同士で衝突しない安全なマスに各 Queen を配置することを考えます。また求めるパターン数は、パターン間の対称性を考慮します。つまりチェスボードを回転して同じパターンになる場合や、鏡のように左右、上下対称となる場合は同一のパターンとみなします。このN-Queens 問題は古くから解かれており、1850 年に正しい答えが見つかりました。また 1969 年に初めて一般的な解法が発見されています。
インテル® Parallel Composer に付属する N-Queens サンプル・プロジェクトでは、この N-Queens 問題に対して複数の並列化手法を使用したサンプルが紹介されています。本章ではこのプロジェクトの中で並列化される
前のベースプロジェクト（nq-serial）のソースコードを使用して、本製品の基本使用方法を前項のワークフローに沿って説明します。
使用するサンプル・ソース・コード（nq-serial.cpp）の内容を簡単に説明します。
以下にサンプルコードの主要関数を記載していますが、まず main() 関数の引数としてチェスボードのサイズ（N × N）のマス数が入力データとして指定されています。また main() 関数内で N-Queens 問題を解くための関数 solve() がコールされ、この関数の計算時間を timeGetTime() 関数を使用して計っています。solve() 関
数ではループ文にて計算エンジン関数 setQueen() が N マス回実行されます。このループでは、チェスボー
ドの列の値（i）をインクリメントしており、Queen のポジション（0 行、i 列）を初期計算値として setQueen()関数に与えられています。そして setQueen() 関数では、この初期値から Queen を安全に配置できる次の（行、列）を求めるため、 setQueen() 関数を再帰的にコールしています。そしてこの再帰処理で見つかったパター
ンの数が随時グローバル変数である nrOfSolutions に加算され最終的な解答が求められます。

6 / 48int main(int argc, char*argv[]) {if(argc !=2) {cerr << "Usage: nq-serial boardSize [default is 8].¥n";size = 8;} else {size = atoi(argv[1]);// チェスボードの ’N’ マスサイズ}
cout << "Starting serial recursive solver for size " << size << "...¥n";
DWORD startTime=timeGetTime();solve(new int[size]);// N-Queens 問題を解くための関数コール

DWORD endTime=timeGetTime();cout << "Number of solutions: " << nrOfSolutions << endl;
cout << "Calculations took " << endTime-startTime << "ms.¥n";return 0;}

void solve(int queens[]) {for(int i=0; i<size; i++) {// try all positions in first row// create separate array for each recursion
setQueen(queens, 0, i);// 計算エンジン関数を N マス回コール}}
void setQueen(int queens[], int row, int col) {for(int i=0; i<row; i++) {// vertical attacks
if (queens[i]==col) {return;}// diagonal attacks
if (abs(queens[i]-col) == (row-i) ) {return;}}// column is ok, set the queen
queens[row]=col;if(row==size-1) {nrOfSolutions++;// N-Queens 問題の答えを格納}
else {// try to fill next row
for(int i=0; i<size; i++) {setQueen(queens, row+1, i);}}}

7 / 48// 次の行を計算する再帰関数

３−３．サンプルコードのビルド（インテル® Parallel Composer 使用）
まず、サンプルコードをビルドして実行ファイルを作成する必要があります。ここでは VS2008 でプロジェク
トを新規作成してプロジェクトのビルドを実行します。また、コンパイラーはインテル® Parallel Composer に含まれるインテル® C++ コンパイラーを使用して、コンパイル手順を説明します。
＜ビルド手順＞
1. まず、Windows の [スタート] メニューから VS2008 を起動します。
ご注意： Microsoft Windows Vista を使用している場合は、下図のようにショートカットを右クリック
して表示されるメニューから [管理者として実行] を選択してください。

Note：[スタート] メニューから [Intel Parallel Studio] – [Intel Parallel Studio with VS 2008] を選択して起動することもできます。

2. プロジェクトを新規作成します。作成するプロジェクトは、空の「Win32 コンソール アプリケーション」です。プロジェクト名は任意で構いませんが、ここでは nq-parallelize

8 / 48とします。

3. 作成したプロジェクトにサンプルコード（nq-serial.cpp）を追加します。このサンプルコードは後で変更するため、オリジナルのコピーを保存しておくことをお勧めします。

4. 次にプロジェクトの構成を Debug から

Releaseに変更して、一度 VS2008 の VC++ コンパイ

ラーでこのサンプルをビルドしてみましょう。ビルドに際して、本サンプルコードでは timeGetTime()の時間関数を使用していますので winmm.lib ライブラリーをリンカーに追加指定する必要があります。下図のようにプロジェクトのプロパティページから追加してください。

5. プロジェクトのビルドを行い、正常終了していることを確認してください。

9 / 48

6. 次に、作成された実行形式ファイルの実行に入りますが、その前に本サンプルコードはチェスボード
のサイズを引数としているので、プロジェクトのプロパティページの「コマンド引数」に値を指定します。ここでは 13 とし 13 マスのチェスボードの計算とします。

7. それでは VS2008 のメニューから、[デバッグ] - [デバッグなしで開始] を選択して実行してみます。
以下のような結果が表示されます。

8. 次にインテル® Parallel Composer に含まれるインテル® C++ コンパイラーを使用してビルドを行ってみましょう。インテル® C++ コンパイラーでコンパイルする場合はまずプロジェクトを変換する必要があります。変換方法は以下のツールバーの変換ボタンをクリックします。変換確認メッセージが表示されるので同意して変換を行います。VC++ プロジェクトへの再変換も可能です。

インテル® C++ プロジェクトへ変換

VC++ プロジェクトへ変換

10 / 48

9. 変換が完了したらビルドを行う前にプロジェクトのプロパティページを開いて内容を確認してみましょう。インテル® C++ プロジェクトへの変換後はプロジェクトページの内容も変換され以下のように英語で表示されます。

10. それではプロジェクトをクリーンしてからビルドを行ってみましょう。以下のように出力ウィンドウからインテル® C++ コンパイラーでコンパイルされていることが確認できます。

11. VS2008 のメニューから、[デバッグ] - [デバッグなしで開始] を選択して実行してみましょう。

VC++ コンパイルした結果と比べると実行速度が速くなっていることが確認できます。インテル® C++
コンパイラーではデフォルトで適切な最適化オプションが付加されているのでコンパイルするだけでもこのようにある程度のパフォーマンスを得ることができます。

11 / 48

３−４．ホットスポットの検出（インテル® Parallel Amplifier 使用）
プロジェクトのビルドが完了しアプリケーションが作成できたら次にインテル® Parallel Amplifier を使用してアプリケーションのホットスポットを調べます。ホットスポットとはアプリケーション内でプロセッサー負荷の最も大きい処理を指します。このホットスポットをインテル® Parallel Amplifier で検出し、並列化の対象となる処理を見極めます。ここでは、前項でビルドしたサンプル・アプリケーション（nq-parallelize.exe）を使用してホットスポットの検出方法を説明します。インテル® Parallel Amplifier の起動方法は非常に簡単で、以下のインテル® Parallel Amplifier のツールバーで、[ Hotspot – Where is my program spending time?（”ホットス
ポット − プログラム中で最も時間を消費している場所は？”）]を選択して [Profile] ボタンをクリックするだ
けです。

[Profile] ボタンをクリックするとインテル® Parallel Amplifier はアプリケーションを実行してホットスポットを検出するためのデータ収集を開始します。アプリケーションの実行が終了すると同時にデータ収集を終了し収集データを集計して結果を下図のように表示します。

12 / 48

表示される結果には、「Hotspots」、「Call Stack」や「Summary」ウィンドウがあります。
まず右図の「Summary」ウィンドウについて説明します。
本ウィンドウにはアプリケーション実行における概要を示しています。各表示項目の意味は以下のとおりです。
z Elapsed Time … アプリケーションの実行にかかったシステ
ム経過時間。単純にアプリケーションの完了時間 − アプリケーションの開始時間 。

システム上の他のアプリケーションの実行状況にも影響される。
z CPU Time …

アプリケーションの実行に費やした CPU 稼働時間。マルチスレッド・アプリケーショ
ンで複数の CPU コアーなどが使用された場合は、各 CPU コアー時間の合計時間となる。スレッド間の同期や I/O などによる待ち時間は含まれない。

z Unused CPU Time … アプリケーションの実行に使用されなかった CPU の合計時間。待機時間や、またこの例のようにマルチコアーシステム（Intel(R) Core(TM)2 Quad）でシングルスレッ
ド・アプリケーションを実行した場合は、使用されていない CPU コアーの時間も加算される。
z Core Count …

システムに搭載される（論理）コアー合計数

z Threads Created … アプリケーションのスレッド数「Call Stack」ウィンドウには、検出された Hotspot に対する関数トレースが表示されます。このトレースにより Hotspot までの関数経路を追うことができます。関数経路が複数ある場合は別ページで表示されます。
以下の例では 9 枚、関数経路のページが存在しています。またページ内の特定の関数の行をダブルクリック
すると、以下の図のようにその関数のソースコードが表示されます（ソースコードが存在する場合）。

13 / 48

Note：この「Call Stack」の関数経路に、表示されるはずの関数がない場合があります。実際このサンプルの場合も、main 関数からコールされるはずの solve 関数がありません。これはコンパイラーによって関数がインライン展開されているからです。このインライン展開による最適化を外したい場合は、プロジェクトをデバッグモードでビルドするか、または下図のようにインテルコンパイラーの IPO 最適化オプションを外すか、インライン関数展開のオプションを変更してお試しください。

「Hotspots」ウィンドウには、目的のホットスポットが「Bottom-up」ウィンドウと「Top-down Tree」ウィンドウが含まれます。「Bottom-up」ウィンドウでは、検出された Hotsport 関数を基準に内部関数から上位関数への（下から上への）関数コールシーケンスを表示します。「Top-down Tree」ウィンドウでは逆に、上位関数から Hotsport 関数に向かって内部関数への（上から下への）関数コールシーケンスが表示されます。

14 / 48

「Hotspots」ウィンドウに表示される Hotspot 関数

setQueen の表示をダブルクリックすると以下のように setQueen 関数のソースファイルが表示され主要ラインに対して CPU タイムが表示されます。このサン
プル・アプリケーションの場合は 107 行目の関数の出口で最も CPU 時間を費やしていることが確認できます。また、89 行目の for ループ内の各処理でも CPU 時間を費やしていることが分かります。

本サンプル・アプリケーションの場合は、このホットスポットの結果より、行ったほうが効果的であると考察できます。つまり

setQueen

関数ごと並列化を

setQueen 関数をコールしている solve関数に対してマルチスレッドによる並列化を実装します。
Note：一般的に、マルチスレッドによる並列化を行う場合は可能な限り大きな領域を処理単位としたほうが効果的となります。

なお、上図のソースコード・ウィンドウ内のコードラインをダブルクリックすると Visual Studio のソースコー
ド画面にジャンプすることができます。それでは本サンプル・アプリケーションに対してマルチスレッドによる並列化を行ってみましょう。

15 / 48

３−５．マルチスレッドの実装（インテル® Parallel Composer 使用）
インテル® Parallel Studio がサポートするマルチスレッド方法には大きく以下の 3 種類があります。
１）Win 32 スレッド API
２）OpenMP３）インテル® Threading Building Blocks（インテル TBB）
Win 32 スレッド API は、マイクロソフト社が提供するスレッド・ライブラリーで、CreateThread 関数や_beginthread 関数などを使用してスレッドの生成を行う最も基本的なマルチスレッド方法です。この方法を使用した場合は、スレッドの作成や管理、またスレッド間の同期処理などきめ細かな制御ができ、プログラマー
にとって自由度の高いマルチスレッド設計が可能となります。しかしその反面、コーディング量の著しい増加やメンテナンス性の低下、また複雑化するプログラミングに伴う不具合の増加が発生します。一般的に Win 32
スレッド API は、他の２つのハイレベルな言語に対して、並列化のアセンブリ言語と位置づけられます。
OpenMP は、マルチスレッドアプリケーション開発のための業界標準規格です。この OpenMP では、既存のシリアルコードに #pragma 指示キーワードを使用してマルチスレッドを実現します。この方法を利用した場合の利点は、何といってもコードの修正量が極めて少ないということです。Win 32 スレッド API での細かなスレッド制御は OpenMP で規定された動作で処理されるでプログラマーの負担が軽減されると同時にプログラ
マーはシリアルコードからパラレルコードへの変換作業に集中することが出来ます。また OpenMP はランタ
イムでプロセッサーコアー数などを自動で検出しますので実行環境に適したパフォーマンスを発揮します。またコンパイラーレベルで認識する #pragma 指示キーワードを使用するスタイルのため、コンパイラーオプ
ションによってマルチスレッド実装の制御が可能となり、たとえば開発中におけるデバッグの際は #pragma
指示キーワードを無視するようにコンパイルを行い、シリアルコードでの処理の確認が出来るようになります。
さらに、システムアーキテクチャーや OS 間での移植も容易になるという利点もあります。OpenMP では、マスタースレッドが並列実行領域内でスレーブスレッドの生成と完了を暗黙的に行っているため Fork-Join モ
デルのマルチスレッド方法とも呼ばれています。
インテル® TBB は、インテル社が提供する C++ プログラマー向けのマルチスレッド・ライブラリーです。このインテル® TBB は、STL のようにテンプレート･ライブラリーとして提供されます。インテル® TBB では多数の並列化用のテンプレート・クラスが定義されており、プログラマーはこれらのテンプレート・クラスを適用することにより、スレッド制御を特に意識することなくハイパフォーマンスなマルチスレッドを実装することが可能となります。また、インテル® TBB は、タスクベースな並列化概念で設計されており、OpenMP と比較すると、さらに詳細な設定を行うことができます。C++ のオブジェクト指向言語を使用するプログラマー
は、この方法を使用してマルチスレッドを実装することになります。

16 / 48

ここでは、前項のホットスポットの検出で得た考察を基に、サンプルコードのsolve 関数に対して OpenMPを使用したマルチスレッドの実装方法を説明します。
OpenMP では、たくさんの構文や宣言子などが定義されています。OpenMP を使用するには、まず以下の宣言
子を使用して並列実行領域を明確化する必要があります。
#pragma omp parallel{〜並列実行領域〜}
この領域内に記述されるコードは基本的に複数のスレッドによって実行される処理となります。たとえばこの領域に printf 文があった場合、生成される各スレッドで処理が実行されるので複数の printf 出力が表示されます。この生成されるスレッド数はデフォルトでは OS にて認識されるプロセッサー・コアー数になります。
並列実行領域構文のほかに、ここではワークシェアリング構文を使用する必要があります。ワークシェアリング構文では、並列実行領域内でワークロード（処理されるべき仕事量）をスレッド間で分担します。この分担方法（スケジュール）は特に指定がない限り自動で配分されます。複数のワークシェアリング構文が存在しま
すが、ここでは以下の for 宣言子を使用します。
#pragma omp forFor( i=0; i<N; i++ ) // 0 〜 N までのワークロードをスレッド間で自動分配{・・・仕事内容・・・}
本サンプルでは、上記で説明した並列実行領域構文とワークシェアリング構文を同時に宣言して以下のような記述方法で並列化を行います。
void solve(int queens[]) {#pragma omp parallel for
for(int i=0; i<size; i++) {// try all positions in first row// create separate array for each recursion
setQueen(queens, 0, i);}}

上記では、チェスボードのマス数（size）をスレッド間で分担して仕事（setQueen）を並列実行することになります。つまり、 size の値が 13 （i=0〜12）で、4 本のスレッドが利用可能な環境の場合（たとえば、Intel
Core2 Quad Core プロセッサーを使用している場合）、スレッド 1 が i= 0〜3 を担当、スレッド 2 が 4〜6、スレッド 3 が 7〜9、そして スレッド 4 が 10〜12 を担当するように分配されて 4 本のスレッドが同時に、独立して setQueen の仕事をすることになります。次の図は、この並列化ロジックのイメージを示しています。
17 / 48

solve( int queens[])#pragma omp parallel for
Fork

マスター
スレッド

＜スレッド１＞＜スレッド２＞＜スレッド３＞＜スレッド４＞

setQueen(queens,0,0)
setQueen(queens,0,1)
setQueen(queens,0,2)
setQueen(queens,0,3)

setQueen(queens,0,4)
setQueen(queens,0,5)
setQueen(queens,0,6)

setQueen(queens,0,7)
setQueen(queens,0,8)
setQueen(queens,0,9)

setQueen(queens,0,10)
setQueen(queens,0,11)
setQueen(queens,0,12)

Join

それでは、上記のように#pragma omp parallel for 構文を for ループ文の直前に記述してビルドしてみま

しょう。OpenMP を使用したコードをコンパイルする場合は、コンパイラーに#pragma ompの指示キー

ワードを認識させる必要があります。インテル® C++ コンパイラーでは /Qopenmp コンパイルオプションを指定することによりコンパイルが可能となります。 nq-parallelize

プロジェクトのプロパティページを開いて下図のように OpenMP による並列化を有効にしてください。

オプション設定を保存してビルドを行い、正常終了を確認して実行してみてください。以下のような結果が表示されます。

N-Queens のサイズ 13 の答えは、73712 であるため、ここで得られた解答は間違っています。また並列化
したにもかかわらず実行時間が並列化する前よりも逆に遅くなっていることが確認できます。
それでは、次にこの OpenMP により並列化処理が施されたサンプルコードのデバッグを行ってみましょう。

18 / 48

３−６．マルチスレッドコードのデバッグ（インテル® Parallel Composer 使用）
インテル® Parallel Composer に含まれるコンポーネントのひとつに、インテル® Parallel Debugger Extension があります。このインテル® Parallel Debugger Extension は Microsoft Visual C++ デバッガーエンジンに組み込まれ、並列化アプリケーションの効率的なデバッグ環境を提供する拡張機能です。本コンポーネントが提供する主な機能は以下のようなものがあります。
① スレッド間で共有されるデータへのアクセス検出と分析② 再入可能関数コールにおけるプログラムの一時停止（ブレーク）機能③ OpenMP 並列領域に対する動的な（デバッグ中での）シリアル化④ OpenMP ランタイム情報の表示⑤ SSE レジスターの表示
ここでは ① の機能を使用してスレッド間におけるデータアクセスの動作に注目しながらデバッグを行います。インテル® Parallel Debugger Extension の機能を使用する場合はインテル® C++ コンパイラーに /Qopenmp
オプションに加えて /debug=parallel オプションを指定する必要があります。それでは以下に、インテル®
Parallel Debugger Extension によるデバッグ手順を説明します。
１）プロジェクトの構成をDebug

モードにする。

２）プロジェクトのプロパティページで、[構成プロパティ]‐[C/C++]‐[Language]‐[OpenMP Support] を選択して/Qopenmp を指定します。

３）プロジェクトのプロパティページで、[構成プロパティ]‐[C/C++]‐[Debug]‐[Enable Parallel Debug
Checks] を選択して、下図のように

４）

Yes (/debug:parallel)

Release モード同様、リンカーに対して winmm.lib

を指定します。

ライブラリーの追加と、「コマンド引数」に入力値として 13 を指定します。
５）オプションの設定を保存してリビルドを行います。正常終了していることを確認してください。
６）次にインテル Parallel Debugger Extension による検出機能を有効にします。VS2008 のメニューから[デバッグ] - [Intel Parallel Debugger Extension] – [Thread Data Sharing Detection] – [Enable Detection]
をクリックします。または、以下のツールバーよりインテル Parallel Debugger Extension の機能をON／OFF することができます。
19 / 48

[ Thread Data Sharing Events ]クリックで ONクリックで OFF[ Thread Data Sharing Filters ]共有データアクセス検出時にプログラムを一時停止

７）続いて同様に、VS2008 のメニューから [デバッグ] - [Intel Parallel Debugger Extension] – [Windows] –[Thread Data Sharing Events] をクリックし、「Thread Data Sharing Events」ウィンドウが表示されることを確認します。このウィンドウに共有データへのアクセス結果がスレッド別に表示されます。な
お、この操作は、ツールバーからも行うことができます。
８）これでデバッグの準備が完了しましたので、VS2008 のメニューから [デバッグ] - [デバッグ開始] を選択してデバッグを開始します。
デバッグが開始されるとサンプルコードが実行されますが、すぐにインテル® Parallel Debugger Extension により共有データへのアクセスが検出され実行がストップします。「Thread Data Sharing Events」ウィンドウには
以下のような検出イベントの内容が結果として出力されています。

表示される結果には、共有データのアドレス（0x003d6138）、スレッド番号（4700、7140、7860）、動作（read/write） またソース上の行番号などが記載されます。この検出されたイベントでは、アドレス
0x003d6138 から 4 バイトサイズのデータに対して、3 つのスレッドから合計 3 回のアクセスがあったことを意味しています。アクセス内容はソースコード（"nq-serial.cpp”）の 95 行目において、同オブジェクト（0x003d6138）に対する書き込みアクセスが、3 つのスレッド（4700、7140、7860）によって実行されたことが検出されています。アクセス内容の項目をダブルクリックすると表示されているソースコードの行に飛ぶことができます。
82

void setQueen(int queens[], int row, int col) {
...

93
94

// column is ok, set the queen

95

queens[row]=col;// 複数のスレッドから書き込み動作あり

96….

20 / 48

この結果より配列 queens に対して複数のスレッドからのアクセスがあり、データの競合による値の不整合が発生している可能性を読むことができます。
次にこのまま [F5] キーを押下してデバッグを続行してください。すぐ同様な原因が検出されプログラムの実
行がストップするはずです。この queens に対するデータ競合は既に分かりましたので、この処理に関しては
今後検出しないように、インテル® Parallel Debugger Extension に対してフィルターを設定することができます。
下図のように「Thread Data Sharing Events」ウィンドウ内のイベントを右クリックして queens のデータオブ
ジェクトに対する検出処理をフィルターするように設定します。

設定したフィルターの内容は、VS2008 のメニューから [デバッグ] - [Intel Parallel Debugger Extension] –[Windows] – [Thread Data Sharing Filters] をクリックし、「Thread Data Sharing Filters」ウィンドウで確認できます。なお、この操作は、ツールバーからも行うことができます。

このデータオブジェクトのフィルターで注意しなければならないことは、本データオブジェクトの内容がアドレス（0x003d6138）から 4 バイトで指定されていることです。queens 配列の大きさは、int 型（4 バイト）を main() 関数の引数（size = 13）分、定義していますので合計 52 バイトとなっています。よってこのデー
タオブジェクトのサイズを変更する必要があります。変更は、フィルターの内容をダブルクリックして表示される以下の「Modify Data Range Filter」ダイアログで行います。このダイアログで [Byte Count] の値を 4 から 52 に修正して [OK] ボタンをクリックし、変更が反映されたことを確認してください。

21 / 48フィルターの設定が終了したら、再度 [F5] キーを押下してデバッグを続行してください。この修正が各ス
レッドに反映されるまで複数回検出されますが、それ以降はこのフィルター設定が有効になるはずです。
デバッグを続けると今度は、nrOfSolutions データに対してもスレッドデータ共有イベントが発生します。

このイベントで、ソースコード 98 行目の nrOfSolutions++ の処理でもスレッド間におけるデータの競合が起こっていることがわかります。このデータオブジェクトにもフィルターをかけます。ただし、nrOfSolutionsのようなスカラー変数として定義されたオブジェクトに対しては、データサイズの修正は必要ありません。

この状態でデバッグを続行してみるとサンプルコードが最後まで実行されデバッガーが終了するはずです。
本デバッグ作業で、配列 queens と変数 nrOfSolutions に対してデータの競合を防ぐため、何らかの処理を施さなければならないことが分かりました。次の作業ステップとして、コードの修正に進むこともできますが、本ドキュメントではインテル® Parallel Inspector を使用したデバッグ方法もご紹介します。インテル® Parallel
Debugger Extension では、VS デバッガー上でのインタラクティブなデバッグスタイルでしたが、インテル®
Parallel Inspector では、アプリケーションを通常通り実行させて問題点を検出し、結果をサマリーウィンドウに表示する非常にシンプルで使いやすいデバッグツールです。

22 / 48

３−７．マルチスレッドコードのデバッグ（インテル® Parallel Inspector 使用）
インテル® Parallel Inspector を使用すると、データの競合やデッドロックなどのマルチスレッド特有の問題を簡単に見つけることができます。以下にその手順をサンプル・アプリケーションを使用して説明します。
１）まず、プロジェクトのプロパティページを開いて、前項で指定したインテル® Parallel Debugger
Extension 用のコンパイラーオプション（/debug:parallel）を外します。
２）またプロジェクトのプロパティページで、[構成プロパティ] – [デバッグ] – [コマンド引数] の値を 13からたとえば 8 に下げます。これは、インテル® Parallel Inspector を使用してデバッグを行う場合は、大きな入力データは必要ないためです。
３）プロパティページの設定が完了したらプロジェクトをリビルドし正常終了を確認します。
４）VS2008 のメニューから [ツール] - [Intel Parallel Inspector] – [Inspect Threading Errors] を選択します。
または、以下のツールバーから

Threading errors

を選択して [Inspect] ボタンをクリックします。

５）「Configure Analysis」が表示されるので、下図のように検出レベルのゲージをドラッグして、最大のWhere are all the threading problems Inspector can find? にセットします。

23 / 48

６）次に [Run Analysis] ボタンをクリックしてサンプル・アプリケーションのスレッドエラー検出を開始
します。
７）検出処理が完了すると「Event Log」ウィンドウが表示されるので、 [Interpret Result] ボタンをクリッ
クして分析結果を確認します。

結果が以下のように表示されます。デフォルトで「Overview」ウィンドウが開かれ、分析結果の全体像を把握することができます。

「Overview」ウィンドウには、「Problem Sets」と「Observations in Problem Set」の 2 つのペインが上下に表示され、まず「Problem Sets」ペインで解決すべき問題の個数と概要、優先順位を確認することができます。
「Observations in Problem Set」ペインでは「Problem Sets」で表示されるそれぞれの問題に対する詳細が表示されます。上図の例では、データ競合（Data race）の問題が 2 つ検出されており、選択されている優先度 P1の問題に対する内容が表示されています。「Observations in Problem Set」ペインで内容を確認してみると、ID
24 / 48

“X7” と “X8” にて nq-serial.cpp ソースコードの 95 行目の setQueen() 関数内で複数のスレッドによる書き込み処理（Write）が行われデータの不整合が発生する可能性を認識することができます。また「Observationsin Problem Set」ペイン内の＋アイコンをクリックするとそれぞれの項目に関するソースコードの一部が表示されるのでさらに具体的な問題内容を把握することができます。

上図の例では、P1 の問題は queens 配列に関するデータ競合問題であると分かります。また 「Observationsin Problem Set」ペインに表示される各項目間における関係も表示することができます。たとえば上図の例で ID
X7 の項目をダブルクリックするとこの項目を基準とした他の項目との関連性を「Sources」ウィンドウで確認することができます。

25 / 48上図では、ダブルクリックした X7 の内容が「Focused observation」に表示され、それ以外の項目が「Related
observation」ペインに表示されます。この「Sources」ウィンドウで表示される関係図により、スレッド間で起こりえるデータへのアクセス競合を示唆することができます。なお、「Related observation」の内容は、下の「Observations in Problem Set」ペインから別な項目をダブルクリックして表示を切り替えることができます。
また同様に項目を右クリックして表示されるメニューから、「Set as Focus Observation」を選択すればメインとなる「Focused observation」の表示内容も変更でき、問題点への見方を切り替えることも可能です。
このように、インテル® Parallel Inspector の分析結果を基に、スレッド間で起こりえる問題や現象、またソー
スコード内の修正場所を明確にすることができます。
本サンプル・アプリケーションの場合は、データ競合に関する問題が 2 箇所検出され、一つ目の P1 は queens
配列、もうひとつの P2 は nrOfSolutions 変数ということがこの分析結果より認識することができました。これら 2 つの問題についてソースコードを修正する必要があります。次項では実際にソースコードの修正を行います。
Note：インテル® Parallel Inspector によるスレッドエラーのデバッグと、インテル® Parallel Debugger
Extension によるデバッグ方法とでは、それぞれ長所と短所があります。まず、インテル®
Parallel Inspector は、OpenMP だけでなく、Win 32 スレッド API や インテル® TBB を使用したプログラムに対してもデバッグ可能ですが、インテル® Parallel Debugger Extension は、OpenMP プログラムのデバッグに限られます。また、インテル® Parallel Inspector のデバッグ
方法は非常にシンプルでアプリケーション全体に対するスレッドエラーが簡単に把握できます。一方、インテル® Parallel Debugger Extension によるデバッグの利点は何といってもステッ
プ・バイ・ステップでスレッドエラーが追える通常のデバッグスタイルにあるといえます。
たとえば、プログラム実行途中にクラッシュしてしまう場合は、クラッシュ前のスレッドの動作や変数の値を確認しながら原因を追究することができます。

26 / 48

３−８．マルチスレッドコードの修正（インテル® Parallel Composer 使用）
本サンプルコードでは、queens 配列、および nrOfSolutions 変数においてスレッド間でデータの競合問題が発生していることがわかりましたので、ソースコード内のこれらのデータに関する修正を行う必要があります。
修正に当たって本サンプルコードでの OpenMP におけるデータの有効範囲を考える必要があります。OpenMP
では、基本的に並列実行範囲外で定義されたデータは、スレッド間で共有データと認識されます。つまり以下に示すように、queens 配列、および nrOfSolutions 変数は共有データとなり、スレッド間で同じタイミング
での書き込み処理が発生する可能性があるデータとなります。
nrOfSolutions：グローバル変数、つまり OpenMP の並列実行領域外

int nrOfSolutions=0;

int main(){ …
solve( new int[size] );…
solve( int queens[])}{#pragma omp parallel for
for(int i=0; i<size; i++) {setQueen(queens, 0, i);}スレッド A}で定義されているのでスレッド間で共有される

queens[]：main 関数内、つまり OpenMP の並列実行領域外で定義されているのでスレッド間共有データとなるスレッド B

void setQueen(int queens[], int row, int col) {…
// column is ok, set the queen
queens[row]=col;
データ競合！
if(row==size-1) {nrOfSolutions++;}…

void setQueen(int queens[], int row, int col) {…
// column is ok, set the queen
queens[row]=col;if(row==size-1) {nrOfSolutions++;}…

データ競合！
}}

では問題の対処法ですが、まず queens[] 配列は setQueen() 関数で使用され、安全に Queen の駒を配置できるマス目（行、列）を格納しています。つまりこの配列変数をマルチスレッドで使用する場合は、setQueen()
関数単位で独立したものか、または少なくともスレッド単位で独立した配列変数である必要があります。ス
レッド単位で独立な変数として定義した場合は、スレッド ID などを取得する必要があり修正コード量が増える傾向にあるので、ここでは setQueen() 関数単位で独立した queens[] 配列を定義します。つまり solve() 関
数内の for ループ文の中で setQueen がコールされるたびに新たな queens[] 配列を定義します。
それから、nrOfSolutions 変数に関しては、この変数も setQueen() 関数で使用され、安全に Queen の駒を配置できるパターン（つまり答え）がインクリメント格納されており、この変数は各スレッド間で共有されるべきものです。しかし複数のスレッドによって同時に書き込み処理が行われるとデータの値が正しく反映されないケースが発生します。このデータの不整合を回避するため nrOfSolutions++ の処理に対してスレッド間の27 / 48

同期処理を施す必要があります。この同期処理を行った場合は、常に単一のスレッドのみが対象の処理を実行できるようになり、他のスレッドは現在実行中のスレッドがこの処理を終了するまで待ち状態となります。
OpenMP が提供する同期処理構文にはいくつかありますが、ここでは一般的な critical 宣言子を適用します。
なお、通常このように排他制御が必要な領域をクリティカル・セッションといい、本サンプルコードでは、
nrOfSolutions++

をクリティカルセッションとして扱います。

上記までの修正内容をサンプルコードに反映したものを以下に記します。
int main(int argc, char*argv[]) {…
cout << "Starting serial recursive solver for size " << size << "...¥n";
DWORD startTime=timeGetTime();// solve(new int[size]);main() 関数内では、queens[] 配列の定義はしない
solve();
DWORD endTime=timeGetTime();…
}//void solve(int queens[]) {void solve(void) {#pragma omp parallel for
for(int i=0; i<size; i++) {// try all positions in first row// create separate array for each recursion//

setQueen(queens, 0, i);
setQueen(new int[size], 0, i);}

setQueen() 関数コールのたびに queens[] 配列を新たに定義してスレッド間での競合を防ぐ}

void setQueen(int queens[], int row, int col) {…
// column is ok, set the queen
queens[row]=col;if(row==size-1) {#pragma omp criticalnrOfSolutions++;

共有データ nrOfSolutions へのアクセス競合を防ぐためスレッド間の同期処理を追加する}
else {// try to fill next row
for(int i=0; i<size; i++) {setQueen(queens, row+1, i);}}}

28 / 48

サンプルコードの修正が完了したら、プロジェクトをDebug

構成でリビルドして、再度インテル® Parallel

Inspector のスレッドチェックを試してみてください。今度はエラーが表示されないはずです。
次に、プロジェクトをRelease 構成に変更してリビルドを行い、並列化の効果を確認してみましょう。

ビルドの際はプロジェクトのプロパティページで以下の項目を確認してください。
•[構成プロパティ] – [C/C++] – [Debug] - [Enable Parallel Debug Checks] →•[構成プロパティ] – [C/C++] – [Language] - [OpenMP Support] →No

Generate Parallel Code (/openmp

equiv. to /Qopenmp)•[構成プロパティ] – [Linker] – [Input] - [Additional Dependencies] →•[構成プロパティ] – [デバッグ] – [コマンド引数] →winmm.lib

13

ビルドが完了したら、「デバッグなしで開始」を実行してください。結果が以下のように表示されます。

並列化する前の以下のシリアルコードの結果と比較すると３倍近いパフォーマンス向上を確認することができます。

では次にインテル Parallel Amplifier を使用して、この並列化したサンプルコードの並列性をチェックして更なるチューニングに挑戦してみましょう。

29 / 48

３−９．チューニング（インテル® Parallel Amplifier 使用）
ここでは、並列化アプリケーションに対してさらにチューニングを行ってパフォーマンスの改善に望むわけですが、チューニングを行う前に現在の並列化アプリケーションの並列性を把握する必要があります。この並列性を調べることにより対象のアプリケーションが、搭載されるプロセッサ・コアをどれだけ効果的に利用できているかを知ることができます。一般的にこの並列性の数値が高いほどパフォーマンス性能がよいと言えます。
本チューニングでは、この並列性を高めることを目的とします。
それでは、現在のサンプル・アプリケーションの並列性の測定方法を説明しますが、前項までのサンプル・ア
プリケーションの修正とRelease 構成でのビルドが完了していることを事前に確認してください。

並列性の測定にはインテル® Parallel Amplifier を使用します。
以下のように、インテル® Parallel Amplifier のツールバーから、 [Concurrency – Where is my concurrency poor?
（”並列性 − 並列性の低い場所はどこですか？”）] を選択して [Profile] ボタンをクリックします。

サンプル・アプリケーションが自動で実行されて並列性の測定が開始され、しばらくすると以下のような結果が表示されます。

30 / 48

上図において、「Concurrency」ウィンドウと「Summary」タブの内容を見てみましょう。
まず、右図の「Summary」タブの内容に関してですが、Hotspot を求めたときの「Summary」タブの内容（右下の図）と比較すると、表示情報が増えていることがわかります。
z Wait Time … アプリケーション内のスレッドの待ち時間合計。たとえば、スレッド間の同期処理にかかる
待ち時間や I/O 処理により待たされる時間。
z Wait Count ･･･ アプリケーションにおいて、待ちに関するシ
ステム API がコールされた回数。
今回実行されたアプリケーションはマルチスレッド化されていますので、[Threads Created] の値は、[Core Count] と同じ 4 となっており、また経過時間（Elapsed Time）はシングルスレッドの時の1.303s と比較すると 0.491s と、速度が約 2.5 倍向上していることが分かります。しかしマルチスレッド化することによりスレッド間の同期処理などに 0.340s かかっており、待ちの回数は3971 回発生したということも読み取れます。
また、今回の実行の「Summary」タブにはグラフが表示されています。このグラフではアプリケーションの並列実行状況に関する概要が示されます。このグラフの横軸はスレッドの数で、縦軸はそのスレッド数で同時実行された時間を示します。このアプリケーションの例では、4 本のスレッドで同時実行されていた時間が他のスレッド数での実行時間よりも比較的大きいことが分かります。なお、この各スレッド数単位での同時実行時間は、右図のようにグラフノードにマウス
カーソルを合わせることで表示させることができます。またグラフの上部には、矩形で囲まれた数字が表示されており、ここでは 3.27 と記されています。この数字が並列性の数値結果であり、アプリケーションの実行において使用されたプロセッサ・コアの数の平均値を示しています。この例では、4つのコアに対して、平均 3.27 コアを使用して実行されたということになります。この値はおおよそ（CPU Time/ Elapsed Time）で計算されています。
Note：「Summary」タブに表示されるグラフは、厳密には実行可能状態にあるスレッド情報が表示されます。たとえば右図の場合は、CPU コア数が 4 のシステム
上でスレッド数が 6 のアプリケーションが実行されており、同時実行可能状態にあるスレッド数 5 の時間や、スレッド数 6の時間が表示されています。また平均 CPU 使用数が 3.17 という結果が表示されています。アプリケーションは、システムに搭載される CPU コア数を超えるスレッド数を同時実行できないことに注意してください。

31 / 48

では次に「Concurrency」ウィンドウの内容を見てみましょう。デフォルトでは「Bottom-up」ウィンドウ内に処理時間の多い関数名と、それに対応した CPU 時間が同時スレッド数実行状態によって色分けされて表示されます。

また、右上の矢印 >>のマークをクリックすると同時スレッド数実行単位で内容が分割表示されます。

また、右図のように表示するグループデータを選択して、表示内容を変更することができます。
たとえば、「Function – Thread – Caller Function Tree」を選択した場合
は、右下の図のように、関数に対するスレッドの CPU 時間を表示することができます。この例では、”setQueen” 関数に対して 4 つのス
レッドが動作しており、 また表示されている CPU 時間から、ほぼ均等に処理が実行されていることがわかります。このように表示
内容を変更することにより、各スレッド間のロードバランス・
チェックなどを行うこともできます。

32 / 48

本チューニングでは、この setQueen() 関数における、 Poor とOk の並列レベルを

Ideal レベルに上げることを考えます。マルチスレッド・プログラムにおいて並列性低下の主な原因の一つに、スレッド間の同期処理があります。つまり、あるスレッドが他のスレッドによって処理の実行を待たされる状態が処理の効率性を下げています。インテル® Parallel Amplifier では、この待ち状態の分析も行うことができます。
では今度は、インテル® Parallel Amplifier のツールバーから、 [Locks and Waits – Where is my program waiting?”（”ロックおよび待機 − プログラムの待機場所は？”）] を選択して [Profile] ボタンをクリックします。

処理が完了すると以下のような分析結果が表示されます。

表示内容には、待機オブジェクト名、待機時間、待機オブジェクトの実行回数、またオブジェクトのタイプや実行関数名などの項目が表示され、待機時間が一番大きいオブジェクトがトップに表示されます。この例ではsetQueen 関数内の OMP Critical オブジェクトがトップに表示されています。
Note：下図のスイッチボタンを使用して、ユーザ関数がコールしているシステム関数を表示対象に切り替えることもできます。この例で本スイッチをオフにすると、トップオブジェクトの表
示内容が、システムレベルの関数情報に切り替わります。

次に、トップの待機オブジェクトをダブルクリックすると、setQueen 関数のソースコードが表示されます。

33 / 48並列化の際に追加したクリティカル・セッション内の処理、 nrOfSolutions++ （本サンプルコードの解答）でスレッドの待ちが発生していることがわかります。このスレッドの待機状態をなくすためにはこの変数をスレッド間で共有するのではなく独立した変数としてスレッド単位で用意する必要があります。そうすれば各ス
レッドは他のスレッドに影響されることなく完全に独立した状態で（非同期に）処理を実行することができる
ようになります。それぞれのスレッドでの仕事が完了した後で、スレッド別の変数の値の合計を求めることで最終的な解答を得ることができます。では、このロジックをソースコードに反映してみましょう。本チューニ
ングに当たって、OpenMP 規格で定義される、各スレッドの ID を取得する関数とスレッド数を取得する関数を利用します。またスレッド単位で結果を格納する方法には幾つかありますが、ここでは解答変数を新たに定義します。修正が必要な関数は、solve() 関数と setQueen() 関数です。以下に修正内容を記します。
#include <iostream>#include <windows.h>#include <mmsystem.h>#include "omp.h"
using namespace std;// OpenMP 関数を使用するためのヘッダー
void solve(void) {int thrd_max = omp_get_max_threads(); // 利用可能なスレッド数の最大値の取得

int nrOfSolutions=0;
int size=0;

int *solcnt = new int[thrd_max]();#pragma omp parallel// スレッド単位の解答を格納する変数取得// 並列実行領域定義（Fork）{
int myid = omp_get_thread_num(); // 本関数を実行するスレッド ID の取得#pragma omp for
for(int i=0; i<size; i++) {//setQueen(new int[size], 0, i);
setQueen(new int[size], 0, i, solcnt, myid);// 解答変数、スレッド ID を引数に追加}} // pragma omp parallel// （Join）

for(int i=0; i<thrd_max; i++) {nrOfSolutions += solcnt[i] ;// スレッド単位での結果の合計が解答}}//void setQueen(int queens[], int row, int col) {void setQueen(int queens[], int row, int col, int solcnt[], int id) {// 解答変数、スレッド IDの追加…
if(row==size-1) {//#pragma omp critical//nrOfSolutions++;
solcnt[id]++;// スレッド別に結果を格納。同期処理は不要}
else {
for(int i=0; i<size; i++) {//setQueen(queens, row+1, i);
setQueen(queens, row+1, i, solcnt, id);}}}

34 / 48// 解答変数、スレッド ID の追加

以下は、本チューニング・ロジックのイメージ図です。
void solve( void )＜マスタースレッド＞

int thrd_max = omp_get_max_threads();// 最大スレッド数（Quad Core ではデフォルト”４”が返る）

int *solcnt = new int [thrd_max] ();// スレッド数分のヒープ領域を new で取得（ZERO 初期化つき）#pragma omp parallelFork「並列実行領域」＜スレッド 2＞

＜スレッド 3＞

＜スレッド 4＞myid ( “1” が返る) =myid ( “2” が返る) =myid ( “3” が返る) =＜スレッド１＞myid ( “0” が返る) =omp_get_thread_num();

omp_get_thread_num();

omp_get_thread_num();

omp_get_thread_num();

setQueen(newqueens,0,0,
solcnt, myid(=0))
setQueen(newqueens,0,1,
solcnt, myid(=0))
setQueen(newqueens,0,2,
solcnt, myid(=0))
setQueen(newqueens,0,3,
solcnt, myid(=0))

setQueen(newqueens,0,4,
solcnt, myid(=1))
setQueen(newqueens,0,5,
solcnt, myid(=1))
setQueen(newqueens,0,6,
solcnt, myid(=1))

setQueen(newqueens,0,7,
solcnt, myid(=2))
setQueen(newqueens,0,8,
solcnt, myid(=2))
setQueen(newqueens,0,9,
solcnt, myid(=2))

setQueen(newqueens,0,10,solcnt, myid(=3))
setQueen(newqueens,0,11,solcnt, myid(=3))
setQueen(newqueens,0,12,solcnt, myid(=3))

setQueen(…){…

setQueen(…){…

setQueen(…){…

setQueen(…){…

#pragma omp for}// 同期処理不要// 同期処理不要// 同期処理不要// 同期処理不要

solcnt[myid(=0)]++;…

solcnt[myid(=1)]++;…

solcnt[myid(=2)]++;…

solcnt[myid(=3)]++;
…

}

}

}

Join

for(int i=0; i<thrd_max; i++) {nrOfSolutions += solcnt[i] ;// スレッド単位での結果の合計が解答}

修正が完了したら、リリース構成のままでプロジェクトをリビルドします。
ビルドが完了したら、再度インテル® Parallel Amplifier を使用して待機状態と並列性を測定してみてください。
本チューニングで、setQueen() 関数における待機状態は解消され、並列性も向上しているはずです。
ホットスポットに対する並列化およびチューニングが完了したら、次の新たなホットスポットの検証を行い同様の並列化工程を行ってください。

35 / 48

４．関連情報
本章では、その他の関連情報を紹介します。

４−１．インテル® Parallel Composer
４−１−１．主要コンパイルオプション
オプション

内容

・一般的な最適化オプション/O3
IDE プロパティページ：[C/C++] – [Optimization] – [Optimization]

デフォルトの /O2 オプションに加えて、更にループやメモリーアクセスに関する最適化を行う。特に、ループの中で浮動小数点を多用している場合に効果的になります。それ以外の場合は、/O2 よりも遅くなる可能性もあります。
（例）> icl /O3 main.cpp

・プロシージャー間の最適化（IPO）/Qipo

主に関数のインライン展開を行うオプション。関数インライン展開を行うこと

IDE プロパティページ：により、関数コールのオーバーヘッドの他に、他の最適化オプションの機会を[C/C++] – [Optimization] – [Interprocedural
Optimization]

広げることに貢献します。”Release” 構成ではデフォルトで本オプションはオンに設定されています。

・ベクトル化オプション/Qx{SSE4.2|SSE4.1|SSSE3|SSE3|SSE2|Host}
IDE プロパティページ：[C/C++] – [Code Generation] – [Intel
Processor-Specific Optimization]

インテル・プロセッサーに特化したベクトル化オプション。指定した SSE 命令
セットでコードを生成する。ただし、指定された SSE 命令セットを搭載しないプロセッサー上では動作しない。
（例１）生成される main.exe は、SSE4.2 の命令セットを搭載した CPU（たとえば Core i7 など）でのみ動作する。
> icl /O2 /QxSSE4.2 main.cpp（例２）生成される main.exe は、SSSE3 の命令セットを搭載した CPU（たとえば Core i7 や Core 2 Duo など）でのみ動作する。
> icl /O2 /QxSSSE3 main.cpp（例３）/QxHost を指定した場合は、コンパイラーが開発システム（Host）のプロセッサーを自動検出し、搭載されている最新の SSE 命令セットを使用して main.exe を生成する。これは、開発システムが実行環境である場合に、便利なオプションとなる。
> icl /O2 /QxHost main.cpp

36 / 48

/Qax{SSE4.2|SSE4.1|SSSE3|SSE3|SSE2}
IDE プロパティページ：[C/C++] – [Code Generation] – [Add
Processor-Optimized Code Path]

インテル・プロセッサーに特化したコード、および汎用コード（デフォルトでSSE2 レベル）を生成するベクトル化オプション。このオプションは、/Qx 系のオプションとは対照的に汎用コードも生成し、実行環境によって動的に実行パスを切り替える（ディスパッチする）コードを生成する。
（例１）生成される main.exe は、SSE4.2 の命令セットを搭載した CPU（たとえば Core i7 など）および一般 SSE2 命令セットを搭載した CPU（たとえば Pentium 4、SSE2 搭載 AMD プロセッサーなど）で動作する。
> icl /O2 /QaxSSE4.2 main.cpp（例２）生成される main.exe は、SSE4.2 の命令セットを搭載した CPU（たとえば Core i7 など）および一般 SSE3 命令セットを搭載した CPU（たとえば Core 2 Duo、SSE3 搭載 AMD プロセッサーなど）で動作する。
> icl /O2 /QaxSSE4.2 /arch:SSE3 main.cpp（例３）生成される main.exe は、SSE4.2 の命令セットを搭載した CPU（たとえば Core i7 など）および一般 IA-32 CPU（たとえば Pentium 3 など）で動作する。
> icl /O2 /QaxSSE4.2 /arch:IA32 main.cpp/arch:{SSE3|SSE2|IA32}

一般的に、インテル・プロセッサー以外のプロセッサー用に使用するベクトル

IDE プロパティページ：化オプション。

[C/C++] – [Code Generation] – [Enable
Enhanced Instruction Set]

（例）生成される main.exe は、一般 SSE2 命令セットを搭載した CPU（たとえば Core i7、Core2Duo、Pentium4、AMD Processors など）で動作する。
> icl /O2 /arch:SSE2 main.cpp

・自動並列化オプション/Qparallel
IDE プロパティページ：[C/C++] – [Optimization] – [Parallelization]

このオプションは、コンパイラーがコンパイル時に、ソースコードのループ文
を対象に、「安全に」並列化できる処理を探し出し、自動でマルチスレッド・
コードを生成します。ループ間に依存関係がある場合や、処理データの量が少ない場合などは、この機能は適用されません。
（例）> icl /Qparallel main.cpp

・OpenMP 関連オプション/Qopenmp
IDE プロパティページ：[C/C++] – [Language] – [OpenMP* Support]

ソースコード内の OpenMP 記述子（#pragma omp）を認識します。デフォルト
では、インテルの互換 Openmp 動的ライブラリーがリンク対象となります。な
お、インテルが提供する OpenMP ライブラリーには以下のとおりです。
・互換・動的ライブラリー（libiomp5md.dll）（例）> icl /Qopenmp main-omp.cpp
・互換・静的ライブラリー（libiomp5mt.lib）（例）> icl /Qopenmp /Qopenmp-link:static main-omp.cpp

37 / 48

・レガシー・動的ライブラリー（libguide40.dll）（例）> icl /Qopenmp /Qopenmp-lib:legacy main-omp.cpp
・レガシー・静的ライブラリー（libguide.lib）（例）> icl /Qopenmp /Qopenmp-lib:legacy /Qopenmp-link:static main-omp.cpp/Qopenmp-link

使用するインテル OpenMP ライブラリーを 動的／静的 かを区別するオプ

IDE プロパティページ：なしション。デフォルトでは、動的ライブラリーが使用される。

[C/C++] – [Command Line] – [Additional
Options:]（例）> icl /Qopenmp /Qopenmp-link:static main-omp.cpp/Qopenmp-lib

使用するインテル OpenMP ライブラリーを 互換／レガシー かを区別するオ

IDE プロパティページ：なしプション。デフォルトでは、互換ライブラリーが使用される[C/C++] – [Command Line] – [Additional
Options:]（例）> icl /Qopenmp /Qopenmp-lib:legacy main-omp.cpp/Qopenmp-stubs

ソースコード内の OpenMP ランタイム関数を無視して、代わりに空の関数ライ

IDE プロパティページ：なし

ブラリーをリンクする。OpenMP のソースコードに対し、シングルスレッドの[C/C++] – [Command Line] – [Additional
Options:]

コードを生成する場合に利用するオプション。OpenMP ランタイム関数（例：omp_set_num_threads）を使用していない OpenMP のソースコードの場合は、
単に /Qopenmp オプションを外すだけでシングルスレッド化できるが、OpenMP ランタイム関数を使用しているソースコードの場合はコンパイルエ
ラーとなる。このエラーを解消するために本オプションを使用する必要がある。
（例）> icl /Qopenmp-stubs main-omp.cpp

・レポート関連オプション/Qvec-report{0-5}ベクトル化に関する診断情報をレポートする。

IDE プロパティページ：なし

診断情報のレベルを 0 から 5 まで指定できる。

[C/C++] – [Command Line] – [Additional
Options:]（例）> icl /QxSSE4.2 /Qvec-report3 main.cpp/Qpar-report{0-3}> icl /arch:SSE2 /Qvec-report2 main.cpp
自動並列化に関する診断情報をレポートする。

IDE プロパティページ：なし

診断情報のレベルを 0 から 3 まで指定できる。

[C/C++] – [Command Line] – [Additional
Options:]（例）> icl /Qparallel /Qpar-report3 main.cpp/Qopenmp-report{0-2}

OpenMP による並列化に関する診断情報をレポートする。

IDE プロパティページ：なし

診断情報のレベルを 0 から 2 まで指定できる。

[C/C++] – [Command Line] – [Additional
Options:]（例）> icl /Qopenmp /Qopenmp-report2 main-omp.cpp

・Parallel Debugger Extension オプション/debug:parallel

Parallel Debugger Extension 用にコードを生成する。このオプションは必ず、IDE プロパティページ：/Qopenmp オプションと共に使用する[C/C++] – [Debug] – [Enable Parallel Debug
Checks]（例）> icl /Qopenmp /debug:parallel main-omp.cpp

38 / 48

・静的コード分析（Parallel Lint）オプション/Qdiag-enable:sc-parallel{1|2|3}

OpenMP によって並列化されたソースコードの分析をおこなう。

IDE プロパティページ：このオプションは常に /Qopenmp と共に使用され、OpenMP 記述子の確認、ス[C/C++] – [Diagnostics] – [Level of Source
Code Parallelization Analysis]

レッド間でのデータ競合やデッドロックの可能性をチェックして結果を表示します。
（例）> icl /Qopenmp /debug:parallel main-omp.cpp

39 / 48

４−２．インテル® Parallel Inspector
４−２−１．メモリーチェック機能インテル® Parallel Inspector には、スレッドエラーのほかに、メモリーエラーをチェックする機能があります。
この機能は、シングルスレッドおよびマルチスレッド・プログラムにおいて潜在するメモリーのエラーを検出します。
使用方法はスレッドエラーの検出手順と同様に、Visual Studio 上のツールバーから

Memory errors を選択

して [Inspect] ボタンをクリックします。表示される「Configure Analysis」から検出レベルを設定し、「RunAnalysis」ボタンをクリックしてメモリーチェックを開始します。

結果は、下図のように表示されます。

検出されるエラーの種類には、無効なメモリーアクセス、未初期化変数の読み込み、メモリーリークなどが含まれます。検出対象の詳細は、製品ドキュメントの「Intel(R) Parallel Inspector」−「Problem Type Reference」の章を参照してください。検出対象の種類や検出レベルの内容、またサンプルコードが記載されています。

40 / 48

４−２−２．メモリーチェック機能に推奨されるコンパイルオプション
インテル® Parallel Inspector のメモリーチェック機能において、本機能に影響を与えるコンパイラーオプショ
ンを説明します。
オプション

内容

・推奨オプション/Zi

ソースコードを参照するために、シンボル情報が必要となります。

・影響を与えるオプション/Od

検出されたエラーをソースコード上で正しく表示することができます。最適化
されたバイナリーではソースコードを正しく表示できない場合があります。しかし最適化ありの Release 構成でビルドされたバイナリーに対しても同様に
メモリーチェックを行うことが必要です。

/RTC[su1]

インテル Parallel Inspector で検出できないいくつかのメモリーエラーを検出する場合があります。しかし、このオプションは未初期化メモリーを初期化してしまうのでインテル Parallel Inspector によるメモリー検出を阻害する場合もあります。

・推奨されないオプション/Qtcheck

これはインテル Thread Checker 用のオプションであり、インテル Parallel
Inspector では使用しません。

/Qtprofile

これはインテル Thread Profiler 用のオプションであり、インテル Parallel
Inspector では使用しません。

/debug:parallel

このオプションはインテル Parallel Debugger Extension 用です。インテル
Parallel Inspector では使用しません。

・特に影響のないオプション/FIXED:NO

このオプションは インテル Parallel Inspector では使用しません。

/MDd, MD, MT, MTd

どのバージョンの C ランタイム関数を使用しても構いません。このオプション
は、スレッドチェック機能のほうに影響を与えます。

/Qopenmp-link

どのバージョンの OpenMP ライブラリーを使用しても構いません。このオプションは、スレッドチェック機能のほうに影響を与えます。

/D"TBB_USE_THREADING_TOOLS" このオプションは、スレッドチェック機能のほうに影響を与えます。

41 / 48

４−３．インテル® Parallel Amplifier
４−３−１．推奨されるコンパイルオプション
インテル® Parallel Amplifier は、基本的に全てのネイティブバイナリーに対してプロファイリングを行うことができますが、ここでは、プロファイリングに影響を与えるコンパイラーオプションを説明します。
オプション

内容

・推奨オプション/Zi

ソースコードを参照するために、シンボル情報が必要となります。

"Release" ビルドより正確なプロファイリングを行うことができます/MD または /MDd

C ランタイム関数をユーザ・アプリケーションと区別することができます。

・OpenMP アプリケーションに推奨されるオプション/Qopenmp

OpenMP アプリケーションには必須のオプションです。

/Qopenmp-link:dynamic

OpenMP の動的ライブラリーをリンクします。インテル Parallel Composer では、/Qopenmp を使用した場合はデフォルトのオプションとなります。

・TBB アプリケーションに推奨されるオプション/D"TBB_USE_THREADING_TOOLS" インテル Parallel Amplifier が TBB 関数を正しく認識することができます。

・推奨されないオプション/Qtcheck

これはインテル Thread Checker 用のオプションであり、インテル Parallel
Amplifier ではオーバーヘッドとなります。

/Qopenmp-link:static

このオプションは OpenMP の静的ライブラリーをリンクします。本オプション
はインテル Parallel Amplifier では推奨しません。

/Qtprofile

これはインテル Thread Profiler 用のオプションであり、インテル Parallel
Amplifier ではオーバーヘッドとなります。

/Qopenmp_stubs

OpenMP ランタイム関数が無視されます。

/debug:parallel

このオプションはインテル Parallel Debugger Extension 用です。インテル
Parallel Amplifier では使用しません。

・特に影響のないオプション/FIXED:NO

このオプションは インテル Parallel Amplifier では使用しません。

42 / 48

４−３−２．特定箇所のプロファイル方法
インテル® Parallel Amplifier には、Pause（ポーズ）機能と、Resume（再開）機能があり、プロファイルする
処理範囲を特定することができます。
この機能には、手動によるものと、コード内に処理関数を追記して自動で行う方法があります。
まず、以下に手動による手順を説明します。
１）プロファイルを開始する前に、[プロジェクト] – [Intel Parallel Amplifier Project Properties] を選択して以下の 「Projetct Properties」ダイアログを起動します。ダイアログ内の「Start data collection paused（ポーズ状態でデータ収集を開始する）」にチェックをつけます。これを行うことによりアプリケー
ションをプロファイルなしで開始することができます。またその下の「Resume collection after __ sec.
（__秒後に収集開始）」を設定すると、アプリケーションが開始されてから設定した秒数後に自動でプロファイルが開始されるようになります。ここでは、この設定はオフで説明を続けます。

２）上記設定が完了したら、[Profile] ボタンをクリックしてアプリケーションを実行します。
３）インテル® Parallel Amplifier をポーズ状態で開始した場合は、ツールバーが以下のように [Profile] ボタンから [Continue] ボタンに変更されます。プロファイルを開始したい時点で [Continue] ボタンをクリックします。

４）プロファイルを開始すると以下のようにポーズボタンが表示されるので、プロファイルを停止したい
時点でクリックします。

５）他にプロファイルを行いたい箇所がある場合は、上記手順の３）と４）を繰り返すことができます。
アプリケーションを終了して結果を表示する場合は、アプリケーションから停止するか、ツールバーの停止ボタン（■のマーク）をクリックします。

43 / 48

次に、自動による手順を説明します。
１）まず、手動手順の１）同様、「Start data collection paused（ポーズ状態でデータ収集を開始する）」にチェックをつけます。
２）ソースコードの編集を行います。プロファイルを行いたい処理に対してインテル® Parallel Amplifierが提供する Resume 関数（ __itt_resume() ）と Pause 関数（ __itt_pause() ）を以下の例のように追記します。
…
#include “ittnotify.h”// Resume & Pause 関数用ヘッダー…
Func() {…

// プロファイルはポーズ状態__itt_resume();// プロファイル開始

process1();// プロファイルされる処理__itt_pause();// プロファイル停止

process2();// プロファイルされない処理__itt_resume();// プロファイル開始

process3();// プロファイルされる処理__itt_pause();// プロファイル停止}３）プロジェクトのプロパティページにヘッダーファイル（ittnotify.h）のディレクトリーパスを設定します。設定する内容は、デフォルトインストールを行った場合は以下のパスになります。
設定パス： C:¥Program Files¥Intel¥Parallel Studio¥Amplifier¥include４）続いて、ライブラリーの設定を行います。指定するリンク情報は、デフォルトインストールを行った
場合は以下の内容となります。
設定パス： C:¥Program Files¥Intel¥Parallel Studio¥Amplifier¥lib32 (32 ビットシステム)
C:¥Program Files (x86)¥Intel¥Parallel Studio¥Amplifier¥lib64 (64 ビットシステム)
指定ライブラリー： ittnotify_static.lib

44 / 48

５）設定が完了したら、プロジェクトをビルドし直します。
６）ビルドが正常に完了したら、ツールバーの [Profile] ボタンをクリックしてアプリケーションを実行します。結果はプロファイルを行った処理のみ表示されます。

Note：この Pause と Resume 機能を使用して得られる結果はプロファイルを行った処理範囲のみですが、「Summary」タブでは、”Pause Time：” という項目が追加され、プロファイル停止
合計時間とともに、アプリケーション全体の処理結果が表示されます。

45 / 48

５．追加情報
ここでは、本製品に関するその他の情報をいくつかご紹介します。

５−１．ドキュメントの参照方法
製品ドキュメントへのアクセスは、Visual Studio の [ヘルプ] メニューより参照することが出来ます。

Windows の「スタート」メニューからも製品ドキュメントを参照できます。

また、Visual Studio* IDE からは、 [プロパティ ページ] 内の項目に対して F1の項目のヘルプが表示されます。

46 / 48キーに押すことにより、そ５−２．N-Queens サンプルコード
本ドキュメントでは、N-Queens サンプル・プロジェクト内の nq-serial プロジェクトを使用し OpenMP によるマルチスレッド方法を説明しましたが、N-Queens サンプル・プロジェクトにはその他のマルチスレッド手法で記述されたサンプル・プロジェクトが含まれています。以下に、N-Queens サンプル・プロジェクトに含まれる問題解決方法を記します。
① シンプル・シリアル・ソリューション
② STL vector を使用したシリアル・ソリューション
③ Win 32 スレッド API を使用したパラレル・ソリューション
④ __par、__critical などのインテルコンパイラー固有拡張言語を使用したパラレル・ソリューション
⑤ OpenMP 3.0 を使用したパラレル・ソリューション
⑥ OpenMP 3.0 Task 構文を使用したパラレル・ソリューション
⑦ TBB を使用したパラレル・ソリューション
⑧ TBB + ラムダ関数を使用したパラレル・ソリューション

５−３．OpenMP について
OpenMP に関する参考資料および参考サイトをご紹介します。
『インテル® コンパイラー OpenMP* 入門』
http://jp.xlsoft.com/documents/intel/compiler/525J-001.pdf『インテル® C/C++コンパイラー OpenMP* 活用ガイド』
http://jp.xlsoft.com/documents/intel/compiler/526J-001.pdf
・OpenMP ホームページ
http://openmp.org/wp/『OpenMP Application Program Interface Version 3.0 日本語版』（OpenMP 仕様書日本語翻訳版）
http://www.openmp.org/mp-documents/OpenMP30spec-ja.pdf

５−４．TBB について
TBB に関する参考資料および参考サイトをご紹介します。
・TBB 製品紹介サイト
http://www.xlsoft.com/jp/products/intel/perflib/tbb/index.html
・TBB 書籍
http://www.oreilly.co.jp/books/9784873113555/

47 / 48

６．最後に本製品に関するその他の情報は以下のサイトをご参照ください。
http://www.xlsoft.com/jp/products/intel/parallel/index.html
本製品に関してご不明な点がありましたら、下記お問い合わせ窓口より弊社サポートまでご連絡ください。
https://www.xlsoft.com/jp/services/xlsoft_form.html

48 / 48