インテル Parallel Studio XE 2016 Professional Edition -入門
by user
Comments
Transcript
インテル Parallel Studio XE 2016 Professional Edition -入門
インテル® Parallel Studio XE 2016 Professional Edition - 入門ガイド ー エクセルソフト株式会社 www.xlsoft.com Rev 1.0.0 (2016/02/02) 目次 1 はじめに ................................................................................................................................................................................................. 1 1.1 2 基本使用方法 ........................................................................................................................................................................................ 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3 インテル® Parallel Studio XE 2016 Professional Edition 概要 ............................................................................ 1 基本開発工程 .............................................................................................................................................................................. 3 使用するサンプル・プログラム ......................................................................................................................................... 4 プログラムのビルド(Composer Edition 使用) ...................................................................................................... 6 マルチスレッドの設計(インテル® Advisor XE 使用) ........................................................................................... 9 マルチスレッドの実装(Composer Edition 使用) .............................................................................................. 15 マルチスレッドの診断(インテル® Inspector XE 使用) .................................................................................... 17 マルチスレッドコードの修正(Composer Edition使用) ................................................................................... 19 マルチスレッドの動作確認(インテル® VTune™ Amplifier XE 使用) .......................................................... 23 細かなチューニング(インテル® VTune™ Amplifier XE使用)................................................................................. 31 関連情報 .............................................................................................................................................................................................. 35 3.1 インテル® Advisor XE ........................................................................................................................................................... 35 3.1.1 3.2 インテル® Parallel Studio XE 2016 Composer Edition ......................................................................................... 46 3.2.1 3.3 主要コンパイルオプション ...................................................................................................................................... 46 インテル® Inspector XE ....................................................................................................................................................... 49 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4 ベクトル化の向上 ........................................................................................................................................................ 35 メモリーチェック機能 ............................................................................................................................................... 49 検出対象問題の一覧 .................................................................................................................................................... 49 デバッガーでエラーをトラップする方法 .......................................................................................................... 50 検出オーバーヘッドの軽減方法............................................................................................................................. 52 検出中にアプリケーションの状態を確認する方法........................................................................................ 53 推奨されるコンパイルオプション ........................................................................................................................ 54 動的検出 vs. 静的検出 .............................................................................................................................................. 54 インテル® VTune™ Amplifier XE ...................................................................................................................................... 55 3.4.1 3.4.2 3.4.3 サンプリング・メカニズム ...................................................................................................................................... 55 CPI 値 ............................................................................................................................................................................... 56 推奨されるコンパイルオプション ........................................................................................................................ 57 3.4.4 4 追加情報 .............................................................................................................................................................................................. 60 4.1 4.2 4.3 4.4 5 特定箇所のプロファイル方法 ................................................................................................................................. 57 N-Queens サンプル・プログラム ................................................................................................................................. 60 OpenMP について ................................................................................................................................................................ 61 Intel® Cilk™ Plus について ................................................................................................................................................. 61 Intel® TBB について ............................................................................................................................................................. 61 最後に ................................................................................................................................................................................................... 61 1 はじめに 本ドキュメントでは、インテル® Parallel Studio XE 2016 Professional Edition (以下、本製品) を使用して、 プログラムの基本的な最適化手順を説明します。本製品に含まれる各ツールの基本機能および並列化の作業手 順の紹介、および、補足情報や並列化に関する情報も記載しています。 1.1 イ ン テ ル ® Parallel Studio XE 2016 Professional Edition 概 要 本製品は、別製品のインテル® Parallel Studio XE 2016 Composer Edition (以下、Composer Edition)と、下 記のコンポーネントを含む、アプリケーションの最適化、並列化を支援するスイート製品です。 ♦ インテル® Advisor XE 実際にアプリケーションの並列化を実施する前に、並列化の候補となる処理やパフォーマンス向上 の可能性、また並列化によって発生しえる問題などの情報を事前に提供する並列化設計ツールです。 バージョン 2016 よりベクトル化設計ツール機能が追加され、並列化、ベクトル化の両方からプ ログラムの高速化を支援します。 ♦ インテル® Inspector XE プログラム内に潜む「メモリーエラー」やマルチスレッド・アプリケーションで発生する「スレッ ドエラー」に関するチェックをランタイムで行う動的診断ツールです ♦ インテル® VTune™ Amplifier XE プログラム内のボトルネックの調査やマルチスレッド並列実行動作の分析、また CPU キャッシュ ミスやパイプライン・ストール状況、分岐予測ミスなどマイクロアーキテクチャ・レベルでのプロ ファイリングを実施するパフォーマンス解析ツールです 1 / 61 2 基本使用方法 本製品の基本的な使用方法について説明します。本ドキュメントは Windows OS 上での操作を記載していま すが、Linux OS 上でもインテル® Advisor XE、インテル® Inspector XE、インテル® VTune™ Amplifier XE の各ツールに は Windows OS と同様の GUI が用意されています。 Visual Studio を起動し、下図のように新しいツールバーが追加されていることを確認してください。Visual Studio は Windows OS 上で使用できる統合開発環境(IDE)です。 「インテル® VTune™ Amplifier XE 用ツールバー」 「インテル® Advisor XE 用ツールバー」 「インテル® Inspector XE 用ツールバー」 それぞれのツールの役割を理解してマルチスレッド開発工程における位置づけを認識することが大切です。本 章では本製品を使用してマルチスレッド・プログラミングを行う基本的な開発工程(ワークフロー)とサンプ ル・プログラムを使用して、このワークフローに沿った手順でそれぞれのツールの使い方を説明します。 なお、ここでは下記のシステムを使用します。 プロセッサー:Intel® Core(TM) i7-4770(コード名:Haswell コア数: 8 (HT 使用)) OS:Windows 10 Pro (64bit) IDE:Microsoft Visual Studio 2015 Professional Edition 2 / 61 2.1 基 本 開 発 工 程 本製品を使用してプログラムの並列化を行う基本開発工程(ワークフロー)について説明します。 1. 対象のシリアル・アプリケーションに対してインテル® Advisor XE を使用し、並列化導入の設計(デザイ 2. 決定された方針に従って、実際にマルチスレッド処理を実装します。 3. 4. 5. 6. 7. 8. ン)を行い、どのようにマルチスレッド処理を実装するのかの方針を決定します。 Composer Edition を使用してマルチスレッド・アプリケーションをビルドします。 インテル® Inspector XE で、マルチスレッド処理のエラーチェックを実施します。 エラーが存在する場合は、コードを修正して再コンパイルしエラーがないことを確認します。 インテル® VTune™ Amplifier XE で、実装したマルチスレッドを分析してチューニングを行います。 チューニングの余地がある場合は、コードを改善して再コンパイルし効果を確認します。 さらに並列化可能な処理があるか、インテル® Advisor XE を使用して検証します。 インテル® Advisor XE 1. マルチスレッドの設計 Composer Edition 2. マルチスレッドの実装 3. アプリケーションのビルド 5. コードの修正 7. チューニングの実施 インテル® Inspector XE 4. スレッドエラーと 8. 更なる並列化 メモリーエラーのチェック インテル® VTune™ Amplifier XE 6. チューニング 3 / 61 2.2 使 用 す る サ ン プ ル ・ プ ロ グ ラ ム サンプル・プログラムは、インテル® Advisor XE に付属する N-Queens (C++)プロジェクトを使用します。デ フォルト設定でインストールした場合、以下に zip ファイルとしてインストールされます。 C:¥Program Files (x86)¥IntelSWTools¥Advisor XE¥samples¥en¥C++¥nqueens_Advisor.zip (※ zip ファイルは、適当な作業フォルダーにコピーしてご利用ください) この zip ファイルを解凍すると、複数のプロジェクトファイルが生成されます。本章で使用するサンプル・ プログラムは、1_nqueens_serial プロジェクトに含まれる以下のファイルです。 ¥nqueens_Advisor¥1_nqueens_serial¥nqueens_serial.cpp この N-Queens プロジェクトは、ある特定のマス数のチェスボードにおいて、Queen の駒を安全に配置でき るパターンの総数を求める問題です。チェスのルールで Queen は縦、横、斜め(将棋の飛車と角行を合わせ た動き)に動けるので、この動作範囲を考慮して Queen 同士で衝突しない安全なマスに各 Queen を配置す ることを考えます。また求めるパターン数は、パターン間の対称性を考慮します。つまりチェスボードを回転 して同じパターンになる場合や、鏡のように左右、上下対称となる場合は同一のパターンとみなします。 インテル® Advisor XE に付属する N-Queens プロジェクトでは、この N-Queens 問題に対して複数の並列化 手法を使用したサンプル・プリグラムが紹介されています。本章では並列化が実装される前のソースコードを 使用して、本製品の基本使用方法をワークフローに沿って説明します。 nqueens_serial.cpp では、まず main() 関数の引数の値がチェスボードのサイズ (N × N)のマス数として 指定されています。引数が指定されていない場合のデフォルト値は 14 となっています。また main() 関数内 で N-Queens 問題を解くための関数 solve() がコールされ、この関数の計算時間を timeGetTime() 関数を使 用して計っています。solve() 関数ではループ文にて計算エンジン関数 setQueen() が N マス回実行されます。 このループでは、チェスボードの列の値(i)をインクリメントしており、Queen のポジション(0 行、i 列) を初期計算値として setQueen() 関数に与えています。そして setQueen() 関数では、この初期値から Queen を安全に配置できる次の(行、列)を求めるため、 setQueen() 関数を再帰的にコールしています。そしてこ の再帰処理で見つかったパターンの数が随時、グローバル変数である nrOfSolutions に加算され最終的な解 答が求められます。 4 / 61 void setQueen(int queens[], int row, int col) { //check all previously placed rows for attacks 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) { //N-Queens 問題の解答を格納 nrOfSolutions++; } else { // try to fill next row for(int i=0; i<size; i++) { } setQueen(queens, row+1, i); // 次の行を計算する再帰関数 } } void solve() { int * queens = new int[size]; for(int i=0; i<size; i++) { setQueen(queens, 0, i); } // 計算エンジン関数をNマス回コール } int main(int argc, char*argv[]) { if(argc !=2) { cerr << "Usage: 1_nqueens_serial.exe boardSize [default is 14].¥n"; size = 14; } else { // ← チェスボードのデフォルトサイズ size = atoi(argv[1]); if (size < 4 || size > 15) { cerr << "Boardsize should be between 4 and 15; setting it to 14. ¥n" << endl; } size = 14; // ← チェスボードのデフォルトサイズ } (中略) DWORD startTime=timeGetTime(); solve(); // N-Queens 問題を解く関数をコール DWORD endTime=timeGetTime(); (中略) return 0; } 5 / 61 2.3 プ ロ グ ラ ム の ビ ル ド ( Composer Edition 使 用 ) Visual Studio でサンプル・プログラムのプロジェクトを開き、インテル® C++ コンパイラーを使用してビル ドします。 1. サンプル・プログラム(nqueens_Advisor.zip)を適当な作業フォルダーに展開します。 2. “nqueens_Advisor.sln”ソリューションファイルを、Visual Studio で開きます。このときソリューション の変換処理が実行されます。(本ドキュメントでは 3_nqueens_cilk、3_nqueens_omp、3_nqueens_tbb は 使用しないため、プロジェクトから削除しています。) 3. 使用コンパイラーをインテル® C++ コンパイラーに切り替えます。“1_nqueens_serial”を選択した状態で、 [プロジェクト] – [インテル(R) コンパイラー] – [インテル(R) C++ を使用] を選択します。 6 / 61 4. Release モードに切り替えてビルド(リビルド)します。結果は出力ウィンドウで確認します。(エラー 5. [デバッグ] – [デバッグなしで開始] で実行します。マスサイズ14の時の答えが 365596 であることが が発生する場合、次ページの”ビルドエラーが発生する場合”をご参照ください。) 分かります。また、この実行でかかった計算時間も表示されています。 7 / 61 ビルドエラーが発生する場合 下記のエラーが発生してビルドできない場合、nqueens_serial.cpp 内で使用されてい る変数 size を全て boardsize に変更してください。Visual Studio 2015 より C++11 のサポートが拡張され、標準関数として size が含まれるようになったためです。 Visual Studio 上で nqueens_serial.cpp 内の size を右クリックし、”名前を変更”をク リックすると置換ウィンドウが表示されるので、boardsize に置換します。 また、Visual Studio 2015 上で本製品の Update 1 以下のバージョンを使用している場 合、下記のエラーが発生する可能性があります その場合、“1_nqueens_serial”を選択した状態で、[プロジェクト] – [プロパティ] – [構 成プロパティ] – [C/C++] – [コマンドライン]に下記のコマンドをコピー& ペーストして ください。Visual Studio 2015 Update 1 の更新により limits ヘッダーファイルにおけ るマクロの定義が変更されたためです。 -D__builtin_huge_val()=HUGE_VAL -D__builtin_huge_valf()=HUGE_VALF -D__builtin_nan=nan -D__builtin_nanf=nanf -D__builtin_nans=nan -D__builtin_nansf=nanf 8 / 61 2.4 マ ル チ ス レ ッ ド の 設 計 ( イ ン テ ル ® Advisor XE 使 用 ) インテル® Advisor XE を使用して並列化の実装内容を決定します。インテル® Advisor XE は、以下のツールバ ーのボタンをクリックすると起動します。 並列化を行うための機能を使用するには、下記の [Threading Workflow] をクリックします。 表示される「Threading Workflow」に沿って作業を進めることができます。 手順1:Survey Target(ターゲットアプリの調査) まず、アプリケーションを実行して実行時間が多い処理(Hotspot) を検出し、その Hotspot までの関数コールトレースを表示します。 またループ処理が存在する場合はその箇所を別途表示し、並列化箇 所の候補として列挙します。 手順2:Annotate Sources(アノテーションの挿入) 並列化候補の処理に対し、アノテーション(インテル® Advisor XE が提供するマクロ処理)を挿入します。 手順3:Check Suitability(並列化内容の分析) アノテーションが付加されたアプリケーションを実行し、擬似的な 並列動作内容を表示します。この結果を元に並列化性能の期待値を 評価します。 手順4:Check Dependencies(並列化問題の予測) アノテーションが付加されたアプリケーションを実行し、データ競 合などの並列化問題が発生する可能性を確認します。 9 / 61 1. 手順1の「Survey Target」を実行します。 Collect ボタンをクリックして調査を開始します。プログラ ムの実行が完了すると Survey Report 画面が表示されます。この画面では対象のプログラム内で実行さ れる各ループがリストされています。”Top Down”タブをクリックすると関数コールトレースが表示され ます。ループ処理がある箇所は 2. のマークが表示されていることが分かります。 さらに Summary 画面を見ると Top time-consuming Loops の情報が列挙されており、並列処理を実装 する候補として参照することができます。 ここでは、親関数である solve 関数を並列化の対象関数とし、同関数内のループ処理を並列化対象として 検討します。 10 / 61 3. 手順2として、solve 関数内のループ文に対して「アノテーションの挿入」を行います。サンプル・プロ グラムの solve 関数には、あらかじめ必要なアノテーションの内容がコメントとして記載されています ので、このサンプル・プログラムでは3つのコメントを外すことで作業が完了します。(ドキュメントの 都合上、コードに直接関係のないコメントは除外しています。) 上記のようにループ文の外側を ANNOTATE_SITE_BEGIN と ANNOTATE_SITE_END で囲み、ループ内 の setQueen 関数に対して ANNOTATE_ITERATION_TASK を指定することで、次の手順の「Check Suitability」実行の際にループ処理を並列実行領域、setQueen 関数をタスクとして認識します。この場 合、タスクはループの反復回数(size)分存在し、各タスクは同時実行するものと扱われます 4. なお、このアノテーションを使用する場合は、ヘッダーファイル(advisor-annotate.h)をインクルード する必要があります。nqueens_serial.cpp ファイルの上のほうにある以下のコメントを外してください。 //#include <advisor-annotate.h> 5. また、以下の図のように「追加のインクルード・ディレクトリー」に “$(ADVISOR_XE_2016_DIR)/include” を追加します。(今回は既に追加されています。) 設定が完了したら、ビルドを実施して正常終了すること を確認します。 11 / 61 6. アノテーションを記述したアプリケーションに対して「Check Suitability」を実行します。図の Collect ボタンをクリックします。 実行が完了すると以下のような Suitability Report 画面が表示されます。 Suitability Report 画面では上下2つのペインが表示され、上側がすべてのサイト情報(並列実行領域)、 下側には、アプリケーションのスケーラービリティーを表したグラフが表示されます。この画面では、ソ ースコード上に定義した一つのサイト情報が表示されています。プログラム全体のパフォーマンス・ゲイ ンとサイト単位のパフォーマンス・ゲインが示されており、また、タスク実行に関するデータとコア数に 応じたパフォーマンス・スケール情報、またパフォーマンス向上に関するヒントが表示されています。 これらの性能情報を基に、このサイトに対する並列化実装の価値を検討します。 12 / 61 7. 手順4として「Check Dependencies」を実行し、アノテーションで指定した並列化領域内で発生する可 能性のある並列化問題を検証します。「Check Dependencies」を実行する場合は Debug モードが推奨 されており、プロジェクトを Debug モードに切り替え、プロジェクトのプロパティページからインクル ード・ディレクトリーの追加を再度設定してビルドします。 8. 9. Check Dependencies の解析には時間がかかるため計算量を少なくします。プログラムに渡す引数(チェ スボードのサイズ)を 8 に設定します。 図の”Collect”ボタンをクリックして、「Check Dependencies」 を実行します。 実行が完了すると以下のような結果が表示されます。 13 / 61 表示される「Dependencies Report」画面内の、「Problems and Messages」ペインには問題の件数や問 題の種類などが表示され、発生する可能性がある問題が把握できます。ここでは、P3、P4、P5 で Read/Write に関する問題が提示されています。このペインで選択された問題の詳細は、下のペインに表 示されます。また、この問題をダブルクリックするとさらに詳細な画面へと切り替わります。 10. 最後の手順として Summary ページを開き、期待されるパフォーマンス・ゲインと考慮すべき並列化問 題のコストを考えて最終的な決定を行います。ここではこのサイトに対して並列化を実装します 14 / 61 2.5 マ ル チ ス レ ッ ド の 実 装 ( Composer Edition 使 用 ) 前節のマルチスレッドの設計で得た考察を基に、サンプル・プログラムの “solve”関数に対して OpenMP を 使用したマルチスレッドの実装方法を説明します。 OpenMP では多くの構文や宣言子などが定義されており、OpenMP を使用するには、以下の宣言子を使用し て並列実行領域を指定します。 #pragma omp parallel { } ~並列実行領域~ 領域内に記述されるコードは基本的に複数のスレッドによって実行される処理となります。たとえばこの領域 に printf 文があった場合、生成された各スレッド内で実行されるので複数の printf 出力が表示されます。生 成されるスレッド数はデフォルトでは OS にて認識されるプロセッサー・コア数になります。 また、ループ処理を並列化するためには、並列実行領域構文のほかに、ワークシェアリング構文を使用する必 要があります。ワークシェアリング構文では、並列実行領域内でワークロード(仕事量)をスレッド間で分担 します。この分担方法(スケジュール)は特に指定がない限り自動的に配分されます。複数のワークシェアリ ング構文が存在しますが、ここでは以下の for 宣言子を使用します。 #pragma omp for for( i=0; i<N; i++ ) // 0 ~ N までのワークロードをスレッド間で自動分配して処理内容を実行 { } 処理内容 上記で説明した並列実行領域構文とワークシェアリング構文を同時に宣言して、以下のように並列化を行いま す。(※ インテル® Advisor XE で使用したアノテーションは外してあります) void solve() { int * queens = new int[size]; #pragma omp parallel for for(int i=0; i<size; i++) { // try all positions in first row setQueen(queens, 0, i); } } 15 / 61 OpenMP はデフォルトでコア数のスレッドを生成し for ループ文の反復回数(size)をスレッド間で分担し て仕事(setQueen)を並列実行することになります。つまり、“size”の値が 14 (i=0~13)で、生成される スレッド数が 8 の場合、スレッド 0 が i= 0~1 を担当、スレッド 1 が 2~3、スレッド 2 が 4~5 と続いてス レッド 6 が 12、最後のスレッド 7 が 13 を担当するように分配されて 8 本のスレッドが同時に独立して setQueen の実行をすることになります。次の図は並列実行ロジックのイメージを示しています。 Solve() int *queens = new int[size]; #pragma omp parallel for for (int i=0; i < size; i++) Fork スレッド 0 setQueen(queens,0,0) setQueen(queens,0,1) スレッド 1 setQueen(queens,0,2) setQueen(queens,0,3) スレッド 2…6 setQueen... setQueen... setQueen... スレッド 7 setQueen(queens,0,13) .::: Join delete [] queens; 1. “#pragma omp parallel for”構文を for ループ文の直前に記述します。 OpenMP を使用したコードをコンパイルする場合は、コンパイラーに “#pragma omp”の指示キーワー ドを認識させる必要があります。インテル® C++ コンパイラーでは “1_nqueens_serial”プロジェクトの プロパティページを開いて下図のように OpenMP による並列化を有効にしてください。 16 / 61 2. オプション設定を保存してビルドを行い、正常終了を確認して実行します。 N-Queens のサイズ 14 の答えは、365596 であるため、ここで得られた答えは間違っています。なお、得ら れる答えは実行する度に異なった値となります。並列化を実装したことで何か問題が発生しているようです。 この問題は、インテル® Advisor XE の「Check Dependencies」により、ある程度検討がついていますが、動 的解析を実行して確認してみましょう。 2.6 マ ル チ ス レ ッ ド の 診 断 ( イ ン テ ル ® Inspector XE 使 用 ) インテル® Inspector XE は、メモリー関連の問題とスレッド関連の問題を検出することができます。問題検出 を行う場合、Debug モードでプログラムをビルドすることが推奨されており、また、動的検出には大きなオ ーバーヘッドがかかるため、実行サイズを小さくすることで少ない時間で検出できるように調整します。 1. プロジェクトを Debug モードに切り替えます 2. プロジェクトのプロパティページで [C/C++] – [インテル(R) C++] – [OpenMP サポート] に Qopenmp を指定し、[構成プロパティ] - [デバッグ] – [コマンド引数] の値に 8 をセットします。 17 / 61 3. プロジェクトをリビルドし正常終了を確認します。 4. インテル® Inspector XE 用ツールバーから下記の図のボタンをクリックします。 5. 「Configure Analysis Type」画面が表示されるので、下の図のように解析タイプを“Threading Error Analysis”にセットします。 6. 解析レベルを「Locate Deadlocks and Data Races」に設定します 解析レベルは 3 種類用意されており、一番下側がもっとも高い解析レベルとなります。解析レベルが上が ると、より詳細な解析が可能になりますが、オーバーヘッドも大きくなります。 7. [Start] ボタンをクリックしてサンプル・プログラムのスレッドエラー検出を開始します。 検出処理が完了すると下図のような結果画面(Summary 画面)が表示されます。queens ポインタと nrOfSolutions 変数に対するデータ競合が発生していることが確認できます。 18 / 61 8. 表示されている問題をダブルクリックして、さらに詳細な情報をソースレベルで表示します。 対象のソースコードを確認することができるので、次節で問題の修正作業に入ります。 2.7 マ ル チ ス レ ッ ド コ ー ド の 修 正 ( Composer Edition使 用 ) プログラムには queens ポインタおよび nrOfSolutions 変数に対して、スレッド間でデータの競合が発生 していることが判明しました。解決するためにソースコードの修正を行う必要があります。 修正を行うためにはサンプル・プログラムでの OpenMP におけるデータの有効範囲を考えます。OpenMP では、基本的に並列実行領域外で定義されたデータは、スレッド間で共有データとして扱われます。つまり以 下に示すように、queens ポインタ、および nrOfSolutions 変数は共有データとなり、異なるスレッドが同じ タイミングで書き込み処理が行う可能性があるデータとなります。 19 / 61 int nrOfSolutions=0; void solve() { int * queens = new int[size]; #pragma omp parallel for for(int i=0; i<size; i++) { // try all positions in first row setQueen(queens, 0, i); } } nrOfSolutions: グローバル変数、つまり OpenMP の並列実行領域 外で定義されているのでスレッド間で共有される queens: solve 関数内の OpenMP の並列実行領域外で定義 されているのでスレッド間共有データとなる スレッド A スレッド B void setQueen(int queens[], int row, int col) { … // column is ok, set the queen queens[row]=col; Void setQueen(int queens[], int row, int col) { … // column is ok, set the queen queens[row]=col; if(row==size-1) { nrOfSolutions++; } … if(row==size-1) { nrOfSolutions++; } … } } まず queens ポインタは setQueen() 関数内で使用され、安全に Queen の駒を配置できるマス目(行、列) を格納するメモリー領域として使用されています。ただし、このポインタ領域は最終的な答えを求めるための 一時的な作業領域として使用されているため、queens ポインタはスレッド単位で独立したポインタ領域(ス レッドローカルな変数)にすればデータの競合は発生しません。つまり solve() 関数内の for ループ文の内 側に定義することで、ループごとに独立した queens ポインタ領域を生成して setQueen 関数の入力引数と して渡します。 nrOfSolutions 変数も setQueen() 関数で使用され、安全に Queen の駒を配置できるパターンを格納する 変数として使用されています。ただし、この変数は最終的な答えを格納するため計算しながら値をインクリメ ントしていく必要があり、スレッド間で共有すべき変数となります。しかし、複数のスレッドによって同時に 書き込み処理が行われると、データの値が正しく反映されないため、 “nrOfSolutions++”の処理に対してスレ ッド間の排他処理を施す必要があります。 排他処理により、常に単一のスレッドのみが対象の処理を実行できるようになり、現在実行中のスレッドが この処理を終了するまで、他のスレッドは対象の処理にアクセスできない状態となります。OpenMP が提供 する排他処理構文にはいくつかありますが、ここでは atomic 宣言子を適用します。 20 / 61 1. 修正内容をサンプル・プログラムに反映します。queens ポインタの解放処理も、同様にスレッド内で処 理するように修正します。 int nrOfSolutions=0; void solve() { //int * queens = new int[size]; ←コメントアウト #pragma omp parallel for for(int i=0; i<size; i++) { int * queens = new int[size]; ←ループ文の内側で定義 setQueen(queens, 0, i); delete [] queens; ←ループ文の内側で定義 } //delete [] queens; ←コメントアウト } void setQueen(int queens[], int row, int col) { … // column is ok, set the queen queens[row]=col; if(row==size-1) { #pragma omp atomic nrOfSolutions へのアクセス競合を防ぐ nrOfSolutions++; ←共有データ //N-Queens 問題の解答を格納 } ためスレッド間の同期処理を追加する else { // try to fill next row for(int i=0; i<size; i++) { setQueen(queens, row+1, i); } } 21 / 61 次の図は、本修正を施したコードの並列実行ロジックのイメージを示しています。solve() 関数でコールさ れるそれぞれの setQueen() 関数には、スレッドごとに定義された queens ポインタ領域が渡されます。これ によってスレッド間でのデータの競合はなくなります。また setQueen() 関数内の nrOfSolutions++ はスレッ ド間で排他処理が行われ、同時に実行されることはありません。 Solve() #pragma omp parallel for for (int i=0; i < size; i++) Fork スレッド 0 スレッド 1 スレッド 2~6 スレッド 7 int *queens = int *queens = int *queens = int *queens = new int[size]; new int[size]; new int[size]; new int[size]; setQueen(queens,0,0) setQueen(queens,0,2) setQueen... setQueen(queens,0,13) setQueen(queens,0,1) setQueen(queens,0,3) setQueen... delete [] queens; delete [] queens; delete [] queens; delete [] queens; Join 2. サンプル・プログラムの修正後、プロジェクトを“Debug”構成でリビルドして、再度インテル® Inspector 3. プロジェクトを“Release”構成に変更してリビルドを行い、並列化の効果を確認します。 XE のスレッドチェックを行い、エラーが表示されないことを確認します。 ビルドの際はプロジェクトのプロパティページで以下の項目を確認してください。[構成プロパティ] – [C/C++] – [Language] - [OpenMP Support] → /Qopenmp 22 / 61 4. ビルド後、実行します。 [並列化処理の実装後] 並列化する前の以下のシリアルコードの結果と比較すると、ここでは 3.5 倍のパフォーマンス向上がある ことが確認できました。 [並列化処理の実装前] 次にインテル® VTune™ AmplifierXE を使用して、この並列化したサンプル・プログラムの並列性をチェ ックして効率を確認してみましょう。 2.8 マ ル チ ス レ ッ ド の 動 作 確 認 ( イ ン テ ル ® VTune™ Amplifier XE 使 用 ) ここでは、並列化したサンプル・プログラムに対して並列性のチェックを行い、実装した並列化処理につい て確認します。並列性を調べることにより対象のアプリケーションが、システムに搭載される CPU リソース をどれだけ効果的に利用できているかを知ることができます。一般的に、この並列性の数値が高いほどパフォ ーマンスがよいと言えます。 サンプル・プログラムの並列性を、インテル® VTune™ Amplifier XE を使用して確認してみましょう。 ※ 前節までのサンプル・プログラムの修正と“Release”構成でのビルドが完了していることを事前に確 認してください。 23 / 61 1. インテル® VTune™ Amplifier XE 用ツールバーから、下記の図のボタンをクリックします。 2. [Analysis Type] 画面の [Concurrency] を選択します。 3. [Start] ボタンをクリックしてプロファイルの実行を開始します サンプル・プログラムが自動で実行されて並列性の測定が開始します。プログラムの終了後、[Summary] 画面が表示され、解析対象プログラムの実行時間や CPU 時間、Hotspot 関数を確認することができま す。 24 / 61 この画面の中ほどに2つの Histogram が表示されており、対象プログラムの並列性を簡単に把握することが できます。 ① Thread Concurrency Histogram: 横軸はスレッド数、縦軸は経過時間を示しており、アプリケーションの実行において、同時実行され たスレッドの本数とその経過時間の分布を表しています。また同時実行スレッド数によって色分けさ れ、システムが搭載するコア数近辺は“Ideal”つまり理想的な実行と見なされ、逆にスレッド数が少 ② ないエリアは“Poor”つまり不出来な状態を意味します。 CPU Usage Histogram: 横軸は CPU コア数、縦軸は経過時間を示しており、同時に使用された CPU コアの数とその経過時間 の分布を表しています。こちらも性能別に色分けされ、最大コア数近辺での実行は“Ideal”であり低 いコア数のエリアは“Poor”となります。 サンプル・プログラムの場合、両方のヒストグラムで若干のバラつきが見られますが、8 本のスレッドが 8 コア上で実行できている時間が一番多いことが確認できます。バラつきについて、[Bottom-up]画面より詳細 に確認することが可能です。 25 / 61 4. “Bottom-up” タブをクリックし、[Bottom-up]画面を表示します。 [Bottom-up] 画面では、大きく 3 つのペイン (枠) に分かれています。 ① Bottom-up ペイン ② Call Stack ペイン ③ Hostspot 関数や消費 CPU 時間などを表示 関数のコールスタック(呼び出し関係)を表示 Timeline ペイン スレッド単位の実行状態を時系列に表示 Bottom-up ペインには、“setQueen”関数が Hotspot 関数としてリストされており、”setQueen”関数の下に Intel openmp ライブラリー側で使用されているスレッド関連の関数(_kmp_...)が確認できます。ここでは、 “setQueen”関数が CPU 時間のほとんどを消費していることが分かります。また CPU 時間の多くは緑色のバー で示されていることからこの関数の同時実行分布は“Ideal”であることも分かります。 Timeline ペインを見ると、茶色のバーと赤色のバーが表示されています。これはそれぞれ、CPU time (茶) と Spin and Overhead (赤)を示しており、スレッドに対する CPU の実行状態を示しています。CPU Time で は そのスレッドに対してアプリケーションの計算が処理されているのに対して、Spin and Overhead では サンプル・プログラムの実行に付随して発生する余分な処理になり、演算に直接関係ない処理を行っていま す。Timeline ペインから下にある 2 本のスレッド(スレッド 6 と スレッド 7)の動作が他のスレッドと異なっ ていることが確認できます。ここでは、スレッド 6 とスレッド 7 の動作について確認していきます。 まず、Spin and Overhead について確認してみましょう。Timeline の中で大きな Spin and Overhead がスレ ッド 6 とスレッド 7 に存在しています。 26 / 61 5. 6. Timeline ペイン内の OMP Worker Thread#6 と OMP Worker Thread#7 を「Shift キー」を押しながら クリックします。 右クリックして表示されるコンテキストメニュー内から、“Filter In by Selection”を選択します。 すると、スレッド6とスレッド7で実行された処理内容のみが、各ペイン上に反映され、その中で実行 された関数を確認することが可能です。 27 / 61 7. さらに、スレッド6とスレッド7に存在する Spin and Overhead に対してマウスをドラッグさせ、下 記の図のように選択した状態にし、表示されるコンテキストメニュー内の“Filter in by selection”を選択 します 選択後、下記の画面のように、_kmp_fork_barrier 関数のみが表示された状態になります。オーバーヘ ッドの要因が _kmp_fork_barrier によって発生していることが確認できました。_kmp_fork_barrier は OpenMP に実装されている内部関数であり、スレッド間の同期、待機を制御するために使用され ています。 28 / 61 次に CPU time を確認します。Tineline ペインを見ると、既にスレッド 6 とスレッド7では他のスレ ッドと比べ明らかに CPU time が少ないことが確認できます。 そこで、各スレッド内で実行されている関数とその経過時間を調べるために Grouping 機能を使用し ます。Grouping 機能を使用することにより、Bottom-up ペイン内のリスト表記の種類を変更すること が可能です。デフォルトでは “Function / Call Stack” が設定されており、[実行された関数] > [呼び出 し関係] の順で表示されます。 8. Bottom-up ペイン内の上部にある”Function / Call Stack”をクリックします。 9. プルダウンメニュー内から”Thread / Function / Call Stack”を選択します。 29 / 61 10. Bottom-up ペイン内の各 OMP スレッドの横にある とスレッド 6、スレッド 7 のみクリックしています。) をクリックします。(下記の画面ではスレッド 5 画面からスレッド 6 とスレッド 7 は他のスレッドと比べて setQueen の実行時間に2倍近くの差があ ることが確認できます。これは、スレッドの実装時にループ回数について説明したように、総スレッド 数 8 に対して、ループ回数が 14(ボードサイズ)に設計されているためです。スレッド 0 からスレッド 5 までは 2 ボードサイズ分の計算を割り当てており、スレッド 6 とスレッド 7 では共に 1 ボードサイズ 分の計算を割り当てています。 この実行時間の差を解決するためには、さらにループ回数を多くとるように計算アルゴリズムを大幅に 変更する必要があり、高速化するための現実的な方法ではありません。この場合、Concurrency レベ ルの解析ではなく、より詳細な解析によってチューニング可能な箇所を見つけます。 30 / 61 2.9 細 か な チ ュ ー ニ ン グ (イ ン テ ル ® VTune™ Amplifier XE使 用 ) マルチスレッドによる並列化を実装し複数のコアを使用して同時に計算を行うことで、パフォーマンスの向上 を図りました。その結果、CPU 使用率も上がり並列性も向上しました。しかし、これはアプリケーション・ レベルでの効率であり、CPU のアーキテクチャー・レベルで見た場合、つまり CPU 内部で処理される命令レ ベルで考えた場合に、効率的に処理されているかどうかは不明です。 インテル® VTune™ Amplifier XE には、「イベント・ベース・サンプリング(EBS)」と呼ばれるデータ収集 方法があり、インテル®プロセッサーに搭載される PMU(パフォーマンス・モニタリング・ユニット)を使用 して、CPU 内部で発生するさまざまな処理(イベント)情報を収集することができます。イベントは CPU ア ーキテクチャーによって使用できるイベントの種類、イベント数、イベント名が異なり、例えば、下記のイベ ントが収集できます。 CPU クロック数 リタイア命令数 分岐予測ミス L1 / L2 / LLC キャッシュミス キャッシュ スヌープ処理 ITLB / DTLB ミス 各種命令(FP / MMX / SIMD / LOAD / STORE など)のカウント 実行ポート単位のμOP(マイクロオペレーション)数 パイプライン・ストール オフコアイベント インテル® VTune™ Amplifier XE の EBS を使用して、本サンプル・プログラムの動作を確認します 1. [Analysis Type] 画面から [Microarchitecture Analysis] – [General Exploration] を選択します。 [General Exploration]では、インテル®プロセッサーで処理されるイベントの中で、パフォーマンスに関 連のある項目を総合的に取得します。 31 / 61 2. [Start] ボタンをクリックして解析を開始します。結果が表示されたら [Bottom-up] 画面を開きます。 Bottom-up 画面を確認すると、setqueen 関数にて、Bad Speculation と Back-End Bound のセルが ピンク色でマークされています。これはアプリケーションの動作に対して、対象の値の性能が悪化して いる可能性があることを示しています。 Bad Speculation とは CPU 内部のパイプラインに対して、何らかの理由により、ある時間内までに命令 (uOps: micro Operation)を供給できなかったことを示しています。そして、Back-End Bound では、命 令を供給するために必要なリソースが不十分であることを示しており、原因の可能性として、キャッシ ュミスによるストールなどが一例として Intel 社のドキュメントに記載されています。 このピンク色の指摘個所から、キャッシュ関連の問題によって、CPU 側に命令処理能力に対して命令 の供給が追い付いていないため、不要な待機時間が発生している可能性があると考えられます。 ここでは、キャッシュに関係するイベントから問題があるかどうか確認します。これらは先頭に MEM が記載されているイベントで収集します。 3. 解析画面の上部にある”Change”をクリックし、”Hardware Events” を選択します。 32 / 61 選択後、下記の画面が表示され、各イベントの収集結果を関数ごとに確認することが可能です。 4. 画面の中からキャッシュに関係するイベントを探します。ここでは、下記のイベントを確認します。 MEM_LOAD_UOPS_ RETIRED.L3_HIT_PS MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT_PS MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM_PS MEM_LOAD_UOPS_RETIRED.L1_MISS イベントの値を確認すると setQueen 関数内でキャッシュミスが多発していることが確認できます。 33 / 61 5. その行をダブルクリックしてソースコードを開きます。内容を確認すると queens ポインタ内の値と 6. この行に対応した処理をアセンブリで表示して詳細を見ます。アセンブリを表示する場合は、左上にあ col 変数の値を比較している処理でイベント値が増加しています。 る トグルボタンをクリックし、また、 と比較しやすくなります。 ボタンをクリックして上下表示に切り替える 各イベントの値を確認すると、queens ポインタ内の値をレジスタにロードする命令(mov)でイベン トが多くカウントされています。このロード命令は並列実行領域内で実行されるため、フォルス・シェ アリングが発生している可能性があります。 ※ フォルス・シェアリング:同一の CPU キャッシュラインに対して、複数のコアがアクセスを行うこ とでキャッシュミスが発生する問題 これは、キャッシュミスが発生する大きな要因の一つであり、アクセス対象の queen 内の配列がキャ ッシュラインをまたがないようにアライメントすることで解決することが可能です。 34 / 61 queens ポインタは solve() 関数の OpenMP ワークシェア構文内で定義され、for ループの回数分ヒー プからメモリー領域を取得します。この取得するメモリー領域を64バイト境界とすることでスレッド 間でのキャッシュラインの共有を防ぐことができます。ここでは、アライメント付きの動的メモリー取 得関数(_mm_malloc)と解放関数(_mm_free)を使用します。 以下に、queens の 64 バイト境界による論理メモリー上のイメージを示します。 7. ソースコード内の queen ポインタにアライメント処理を行うように修正します。 void solve() { #pragma omp parallel for for(int i=0; i<size; i++) { //int * queens = new int[size]; // コメントアウト int * queens = (int *) _mm_malloc(sizeof(int)*size, 64); // 64バイト境界 // try all positions in first row setQueen(queens, 0, i, thrd_id); _mm_free(queens); // メモリー解放 } } 修正を加えたコードをビルドして実行し、パフォーマンスを確認してください。 3 関連情報 その他の関連情報を紹介します。 3.1 イ ン テ ル ® Advisor XE 3.1.1 ベクトル化の向上 バージョン 2016 より、インテル® Advisor XE にベクトル化の性能向上を支援する機能が追加されました。 対象プログラム内に存在するループ処理を効率よくベクトル化することにより、同じループ処理をより少ない 命令数で実行できるため、大幅な性能向上が期待できます。インテル®コンパイラーは /O2 以上の最適化オプ ションを設定することで、自動ベクトル化機能を使用してループ処理をベクトル化します。 ただし、ループ内の処理や、メモリーアクセスのパターンによって、ループ処理をベクトル化できない場合 が多くあり、その場合には、開発者自身で効率的にベクトル化しやすいようにソースコードを変更する必要が あります。 35 / 61 ここでは、インテル® Advisor XE が持つベクトル化支援機能を使用して、行列の積和演算を行うサンプル・ プログラム内の存在するループ処理をベクトル化し高速化を行います。 下記のサンプル・プログラムを展開し、サンプル・プログラムに含まれる matrix.sln をクリックして、Visual Studio を起動します。 C:¥Program Files (x86)¥IntelSWTools¥samples_2016¥en¥VTune Amplifier XE¥C++¥matrix_vtune_amp_xe.zip 1. ベクトル化支援機能は、インテル®コンパイラーからの最適化レポートを基に情報を取得するため、 2. Release モードでビルドして実行し、プログラムの実行時間を確認しましょう。 matrix プロジェクトのプロパティから、/Qopt-report:3 以上が設定されていることを確認します。 36 / 61 3. インテル® Advisor XE を起動して、ループ処理に対してベクトル化が実装されているかどうか確認しま す。インテル® Advisor XE は、以下のツールバーのボタンをクリックして表示される「Advisor XE Workflow」に沿って作業を進めることができます。以下に手順概要を記します。 手順 1:Survey Target (ターゲットアプリの調査( まず、アプリケーションを実行して実行時間が多いループ処 理を表示し、その中から問題を含むループ処理に対してアノ テーションを設定します。 手順 1.1:Find Trip Counts (ループ回数の調査) ループ処理の反復回数を追加で検出し、レポートに表示しま す。 手順 2.1:Check Dependencies(依存性の調査) アノテーションが設定されたループに対して、データ競合な どのベクトル化の実装を妨げている問題を確認します。 手順 2.2:Check Memory Access Patterns(メモリーアクセ スの調査) アノテーションが設定されたループ処理に対して、メモリー アクセスに関わる依存性をチェックし、メモリージャンプの 可能性があるかどうか確認します。 37 / 61 4. 手順1の「Survey Target」を実行します。 ボタンをクリックして調査を開始します。プログラムの実 行が完了すると以下のような Survey Report 画面が表示されます。 この画面では対象のプログラム内では3つのループ処理が実行されていることが確認できます。また、 リスト内の各ループにある 矢印に注目します。これはそのループがベクトル化されていないこと を示しており、ベクトル化されている場合、 の矢印が表示されます。また、画面下部の[Source]を 確認すると、この 3 つループ処理は 3 重のループ処理であることが確認できます。この中で Self Time が一番大きな 50 行目のループに対して、ベクトル化を実装可能かどうか確認してみましょう。 5. [Find Trip Count] を実行します。 38 / 61 Survey Report のリストに、”Trip Counts”が追加され、各ループ内の反復回数を確認することが可能で す。このプログラムでは、一番内側のループ(50 行目)は、コンパイラーによってループアンロールの最 適化が行われているため、プログラム側で設定されているサイズ 2048 の半分になっています。 6. 50 行目のループから、リスト内の[Vector Issue]行にある[Issue: Assumed dependency present]をクリ ックします。画面下部の情報が、下記のように切り替わります。 ここでは、ループ内に依存性があると仮定されており、ベクトル化を行っていないことが確認できます。 また、より詳しい調査として Dependencies analysis を行うように推奨されていますので、続いて、 手順 2.1 の Check Dependencies を行います。 39 / 61 7. Check Dependencies を行うために、解析対象のループ処理にアノテーションを設定します。 8. ワークフローから、Check Dependencies を行います。 50 行目のループ処理の 行にあるチェックボックスをクリックします。 ただし、Check Dependencies はオーバーヘッドが非常に大きいので、実行完了まで時間がかかります。 依存性などの解析では、プログラムの実行中に対象ループ処理が実行されていれば検出可能なため、こ こでは、30 秒ほど経過した時点でプログラムを終了して結果を確認します。 画面を確認すると、50 行目の処理には、依存性が含まれているとの診断結果が確認できます。さらに情 報を集めるために、手順 2.2 の Check Memory Access Patterns を実行します。 40 / 61 9. Check Memory Access Patterns を実行します。 手順 2.1 の Check Dependencies 同様に、オーバーヘッドが大きいため 30 秒ほど実行させて終了し て結果を確認します。 結果を確認すると、P1 の問題にて “Constant stride” と表示されており、対象のループ内にて非連続 なメモリーアクセスが行われていることが確認できます。リスト内の Stride の数に注目し、その値が 大きいほど性能に悪い影響を与えています。各ループの をクリックすると、該当する処理を表示しま す。このプログラムでは、51 行目の処理について非連続なメモリーアクセスが含まれていることが確 認できます。 以上の情報から、メモリーアクセスと依存性にベクトル化できない問題が含まれているとしてソースコ ードを修正してみましょう。 41 / 61 10. メモリーアクセスについて、51 行目の処理を確認します。 ここでは、配列 a[][]、b[][]、c[][] を使用して積和演算を msize(2048) のループ回数で計算しています。 メモリーアクセスに問題がある場合、”配列のインデックスに使用されている変数”と”一番内側のルー プ変数”に注目します。 一番内側のループでは、変数 k をループ変数として、msize (2048) 回の処理を実行しており、その処 理で変数 k を使用している配列は a[i][k] と b[k][j] になります。C++ では配列要素へのメモリーア クセス順序は行優先になっているため、ここでは、b[k][j] の配列で msize 分のメモリージャンプが発 生していることがわかります。 for(i=tidx; i<msize; i=i+numt){ for(j=0; j<msize; j++) { for(k=0; k<msize; k++) { c[i][j] = c[i][j] + a[i][k] * b[k][j]; } } } 解決するための一番簡単な方法は、一番内側のループと真ん中のループを入れ替えることです。ループ 変数 j は c[i][j]、b[k][j] で使用されており、連続してメモリーアクセスすることが可能です。 for(i=tidx; i<msize; i=i+numt){ for(k=0; k<msize; k++) { for(j=0; j<msize; j++) { c[i][j] = c[i][j] + a[i][k] * b[k][j]; } } } 11. 依存性について、51 行目の処理を確認します。 対象の処理にポインタが使用されている場合、エイリアスの問題が発生していないかどうか考えます。 通常、コンパイラーは関数ベースで最適化処理を行うため、関数内で使用されているポインタが引数と して渡されている場合、そのポインタが他のポインタにエイリアスしていると仮定してベクトル化を行 いません。 42 / 61 このプログラムでは、配列 a[][]、b[][]、c[][] にポインタが使用されており、これらは、matrix.c 内で 確保されたポインタ a、b、cを引数として呼び出されています。そのため、コンパイラーによってエ イリアスの可能性があると判断されています。しかし、matrix.c 内のを確認すると、それぞれのポイン タはエイリアスしていないことが確認できます。 void multiply1(int msize, int tidx, int numt, TYPE a[][NUM], TYPE b[][NUM], TYPE c[][NUM], TYPE t[][NUM]) { ・・・ c[i][j] = c[i][j] + a[i][k] * b[k][j]; ・・・ [matrix.c 内] buf1 = (char *) malloc(NUM*NUM*(sizeof (double))+1024); } ・・・ buf2 = (char *) malloc(NUM*NUM*(sizeof (double))+1024); ・・・ buf3 = (char *) malloc(NUM*NUM*(sizeof (double))+1024); ・・・ a = (array *) addr1; b = (array *) addr2; c = (array *) addr3; ここでは、#pragma ivdep を追加してエイリアスがないことをコンパイラーに知らせます。 for(i=tidx; i<msize; i=i+numt){ for(k=0; k<msize; k++) { #pragma ivdep for(j=0; j<msize; j++) { c[i][j] = c[i][j] + a[i][k] * b[k][j]; } } } 12. 修正後ビルドを行い、再度 Survey Target を実行して、ベクトル化されているかどうか確認します。 43 / 61 結果を確認すると、一番内側のループがベクトル化されていることが確認できます。ベクトル化された 場合には、ベクトル化について [使用した命令セット]、[ベクトル化効率]、[高速化された割合] などの 情報を確認できます。 この結果では、ベクトル化の実装に SSE2 命令セットが使用されていることが確認できます。コンパ イラーの最適化では、デフォルトで SSE2 命令セットのベクトル化を実装します。ここでは、より高 速化されやすい AVX2 命令セットを使用してベクトル化を実装してみましょう。 13. プロジェクトプロパティーより AVX2 命令セットを使用するように設定します。 44 / 61 14. 再度 Survey Target を実行して、AVX2 命令セットを使用してベクトル化されているかどうか確認しま す。 15. 実際に高速化されたかどうか、確認します。 [ベクトル化の実装後] ベクトル化を実装する前の結果と比較すると、ベクトル化の実装によって 17 倍以上の高速化を確認でき ました。 [ベクトル化の実装前] その他に、いままでの修正に加えてデータをアライメントすることによって、高速化することが可能で す。matrix.c 内に既にアライメントされた処理が実装されていますので、メモリー確保の処理を入れ替 えてください。 45 / 61 3.2 イ ン テ ル ® Parallel Studio XE 2016 Composer Edition 3.2.1 主要コンパイルオプション オプション ・高度な最適化オプション /O3 IDE プロパティページ: [C/C++] – [最適化] – [最適化] 内容 デフォルトの /O2 オプションに加えて、更にループやメモリーアクセス に関する最適化を行う。特に、ループの中で浮動小数点を多用している 場合に効果的です。それ以外の場合は、/O2 よりも遅くなる可能性もあ ります。 (例)> icl /O3 main.cpp ・プロシージャー間の最適化(IPO) /Qipo IDE プロパティページ: [C/C++] – [最適化[インテル(R) C++]] – [プロ シージャー間の最適化] ・ベクトル化オプション /Qx{COMMON-AVX512| CORE-AVX512|MIC-AVX512| CORE-AVX2|CORE-AVX-I|AVX| SSE4.2|ATOM_SSE4.2|SSE4.1| ATOM_SSSE3|SSSE3|SSE3| SSE2|Host} IDE プロパティページ: [C/C++] – [コード生成[インテル(R) C++]] – [指定された命令セットの専用コード生成] プログラム全体の最適化。関数のインライン展開などを行う。関数イン ライン展開を行うことにより、関数コールのオーバーヘッドの他に、他 の最適化オプションの機会を広げることに貢献します。本オプション は ”Release” 構成の場合、デフォルトで設定されています。 インテル®プロセッサーに特化したベクトル化オプション。指定した SSE 命令セットでコードを生成する。ただし、指定された SSE 命令セットを 搭載しないプロセッサー上では動作しないので注意が必要。 (例1)AVX 命令セットを使用したベクトル化コードを生成。AVX 命令 セットを搭載しない CPU では動作しない。 > icl /O2 /QxAVX main.cpp (例2)AVX2 命令セットを使用したベクトル化コードを生成。AVX2 命 令セットを搭載しない CPU では動作しない。 > icl /O2 /QxCORE-AVX2 main.cpp (例3) /QxHost を指定した場合は、 コンパイラーが開発システム(Host) のプロセッサーを自動検出し、搭載されている最新の SSE 命令 セットを使用して main.exe を生成する。これは、開発システ ムが実行環境である場合に、便利なオプションとなる。 > icl /O2 /QxHost main.cpp /Qax{COMMON-AVX512| インテル®プロセッサーに特化したコード、および汎用コード(デフォル CORE-AVX512|MIC-AVX512| トで SSE2 レベル)を生成するベクトル化オプション。このオプション CORE-AVX2|CORE-AVX-I|AVX| SSE4.2|SSE4.1|SSSE3|SSE3|SSE2} は、/Qx 系のオプションとは対照的に汎用コードも生成し、実行環境に IDE プロパティページ: よって動的に実行パスを切り替える(自動ディスパッチする)コードを 46 / 61 [C/C++] – [コード生成[インテル(R) C++]] – [指定された命令セットの専用および汎用コ ード生成] 生成する。 (例1)AVX 命令セットを使用したベクトル化コードおよび一般 SSE2 命令セットを使用したベクトル化コードを生成する。実行パス は自動ディスパッチャーによって実行時に決定される。 > icl /O2 /QaxAVX main.cpp (例2) AVX 命令セットを使用したベクトル化コードおよび SSE4.2 命令 セットを使用したベクトル化コードを生成する。実行パスは自 動ディスパッチャーによって実行時に決定される。 > icl /O2 /QaxAVX /QxSSE4.2 main.cpp (例3)SSE4.2 命令セットを使用したベクトル化コードおよび IA-32 命 令(x86/x87 命令)を使用したコードを生成する。実行パスは 自動ディスパッチャーによって実行時に決定される。 > icl /O2 /QaxSSE4.2 /arch:IA32 main.cpp /arch:{SSE2|SSE3|SSSE3|AVX|CO インテル®プロセッサーおよびインテル®プロセッサー以外の CPU で動作 RE-AVX2|IA32} するベクトル化オプション。 IDE プロパティページ: [C/C++] – [コード生成] – [拡張命令セットを 有効にする] (例)一般 SSE2 命令セットを使用したベクトル化コードを生成する。 このオプションは /O2 以上を指定した場合は暗黙的に設定され る。たとえば ”Release” 構成では、デフォルト設定となる。 > icl /O2 /arch:SSE2 main.cpp ・自動並列化オプション /Qparallel IDE プロパティページ: [C/C++] – [最適化[インテル(R) C++]] – [並列 化] インテル®コンパイラーによる自動マルチスレッドコード生成オプショ ン。このオプションは、インテル®コンパイラーがコンパイル時に、ソー スコードのループ文に対して並列化を試み、「安全」に並列化できる場 合、および並列化することによって「効率」が得られる場合に限り並列 化の実装を行います。ループ間に依存関係がある場合や、処理データの 量が少ない場合などは、この機能は適用されません。 (例)> icl /Qparallel main.cpp /Qpar-threshold[:n] (n = 0~100) 自動並列化の効率性の閾値をコントロールするオプション。 IDE プロパティページ:なし [C/C++] – [コマンドライン] – [追加オプショ ン] この閾値を下げることによってインテル®コンパイラーによる効率性の チェックレベルを下げることができ、自動並列化の可能性を高めること ができる。 なお、デフォルトの閾値は 100 に設定されている。 (例)> icl /Qparallel /Qpar-threshold:90 main.cpp 47 / 61 ・OpenMP 関連オプション /Qopenmp IDE プロパティページ: [C/C++] – [言語[インテル(R) C++]] – [OpenMP サポート] /Qopenmp-stubs IDE プロパティページ: [C/C++] – [言語[インテル(R) C++]] – [OpenMP サポート] ・レポート関連オプション /Qopt-report{:1~6} IDE プロパティページ: ソースコード内の OpenMP 記述子(#pragma omp)を認識し、マルチ スレッドコードを生成する。プログラムの実行にはインテル®コンパイラ ーが提供するランタイムライブラリーに含まれる OpenMP 動的リンクラ イブラリー(libiomp5md.dll)が必要となる。 (例)> icl /Qopenmp main-omp.cpp ソースコード内の OpenMP 記述子を無視し、また OpenMP ランタイム 関数については空の関数ライブラリーをリンクする。OpenMP のソース コードに対し、シングルスレッドのコードを生成する場合に利用するオ プション。 (例)> icl /Qopenmp-stubs main-omp.cpp 最適化に関する診断情報をレポートする。 診断情報のレベルを 1 から 5 まで指定できる。デフォルトは 2。 [C/C++] – [診断[インテル(R) C++]] – [最適化 (例)> icl /O3 /Qipo /Qopt-report:3 main.cpp /Qopt-report-phase{:name1, name2, …} レポートを生成する最適化フェーズを指定する。複数指定可能。 診断レベル] IDE プロパティページ: [C/C++] – [診断[インテル(R) C++]] – [ベクト ライザーの診断レベル] /Qopt-report と組み合わせてレベルの指定が可能。 cg ipo コード生成フェーズ プロシージャー間の最適化 (IPO) フェーズ loop ループの入れ子構造の最適化フェーズ offload インテル® MIC アーキテクチャーまたはインテル® グラフィッ クス・テクノロジー向けのフェーズ openmp par pgo OpenMP* フェーズ 自動並列化フェーズ プロファイルに基づく最適化 (PGO) フェーズ tcollect トレース収集フェーズ vec ベクトル化フェーズ all すべての最適化フェーズ。デフォルト設定です。 (例)> icl /O2 /QxAVX /Qopt-report:3 /Qopt-report-phase:par main.cpp 48 / 61 3.3 イ ン テ ル ® Inspector XE 3.3.1 メモリーチェック機能 インテル® Inspector XE には、スレッドエラーのほかに、メモリーエラーをチェックする機能があります。こ の機能は、シングルスレッドおよびマルチスレッド・プログラムにおいて潜在するメモリーエラーを検出しま す。 スレッドエラーの検出手順と同様に、Analysis Type 画面の中でプリセットの“Memory Error Analysis”を選択 し、検出レベルを設定し、そして Start ボタンをクリックしてメモリーチェックを開始します。 3.3.2 検出対象問題の一覧 以下に、インテル® Inspector XE が検出する問題(メモリーおよびスレッド)の一覧を記します。各問題の詳 細は、製品ドキュメントの「Intel® Inspector XE 2016」-「Problem Type Reference」の章を参照してくださ い。 Cross-thread Stack Access(スレッド間のスレッド領域へのアクセス) Data Race(データの競合) Deadlock(デッドロック) GDI Resource Leak(GDI リソースリーク) Incorrect memcpy Call(不適切な memcpy コール(Linux のみ)) Invalid Deallocation(無効なメモリー解放処理) Invalid Memory Access(無効なメモリーアクセス) Invalid Partial Memory Access(無効な部分的メモリーアクセス) Kernel Resource Leak(カーネル・リソースリーク) Lock Hierarchy Violation(ロック処理階層違反) Memory Growth(ある処理区間におけるメモリーの増加) Memory Leak(メモリーリーク:解放不可能) Memory Not Deallocated(メモリーリーク:解放可能) Mismatched Allocation/Deallocation(メモリー取得関数と解放関数の不一致) Missing Allocation(不適切なメモリー解放領域) 49 / 61 Thread Exit Information(スレッド消滅情報) Thread Start Information(スレッド生成情報) Unhandled Application Exception(処理されない例外) Uninitialized Memory Access(未初期化領域からの読み込み処理) Uninitialized Partial Memory Access(部分的未初期化領域からの読み込み処理) 3.3.3 デバッガーでエラーをトラップする方法 インテル® Inspector XE では、エラー検出箇所でデバッガー表示させる機能があります。この時アプリケーシ ョンの実行は停止され、これ以降は通常のデバッガー操作でエラー検出を続行することができます。このエラ ー検出方法によって問題の発生原因を一つずつ調査しながら作業することができます。 ただし、メモリーリークとリソースリーク問題についてはこの方法を利用することができません。これは、こ れらの問題がアプリケーションの終了時に検出可能となるためです。 以下に、このエラー検出を実施する 3 種類の方法を紹介します。 <方法1> 一度エラー検出を行った結果一覧から、特定の問題を選択してそれを右クリックし、表示されるコンテキスト メニューから [Debug This Problem] を選択します。この場合、選択した問題についてのみデバッグ表示され ます。 <方法2> Configure Analysis Type 画面の [Enable debugger when problem detected] を選択してエラー検出を開始 します。 この場合、アプリケーションの問題検出が開始され、問題が検出され次第デバッガーに随時表示されます。 50 / 61 <方法3> 上記の Configure Analysis Type 画面中の [Select analysis start location with debugger] を選択してエラー 検出を開始します。この方法では、検出開始場所を指定することができます。本エラー検出を開始する前に、 検出を開始したい場所にブレークポイントを設定しておきます。アプリケーションがブレークポイントまで実 行されたら、Visual Studio のメニューから [デバッグ] – [Continue With Intel Inspector XE Analysis] を選択 して検出を開始します。 これらの方法を使用して問題が検出された場合、アプリケーションが停止してデバッガーが表示されると共に、 Problem Details ペインも表示されます。このウィンドウには検出された問題の内容が表示されます。また、 この問題に対する操作を行う複数のボタンも用意されています。たとえば [Disable Breakpoint] をクリックす ると、次回同じ問題が発生した際に、その検出を無視することができます。また [Re-enable Breakpoints] を クリックすると、これまでに無効にした問題内容を再度有効にすることができます。 51 / 61 最後に、本機能で使用する2つのウィンドウの表示方法を以下の図に示します。 3.3.4 検出オーバーヘッドの軽減方法 一般的に、動的なエラー検出にはかなりのオーバーヘッドを伴います。このオーバーヘッドを小さくするため にも、まずできるだけ小さな入力値を選択し、実行処理量が小さくなるように工夫する必要があります。 また、検出対象モジュールの範囲を調整してオーバーヘッドを軽減する方法を有効です。Visual Studio IDEメ インメニューから、[プロジェクト]-[Intel Inspector XE Project Properties]をクリックしてプロジェクトプロパ ティーを開きます。モジュールの指定は、一番下にある「Advanced」グループ内の「Modules:」項目の [Modify…] ボタンで指定します。指定したモジュールに対して「Include only the following module(s)」にチェックした 場合は指定したモジュールのみが検出対象となり、「Exclude the following module(s)」にチェックした場合 は指定したモジュールは検出対象から外されます。この方法によってオーバーヘッドが軽減される場合があり ます。 52 / 61 3.3.5 検出中にアプリケーションの状態を確認する方法 アプリケーションが大きくなると検出オーバーヘッドも大きくなる傾向にあり、場合によってはアプリケーシ ョンが動作しているのかハングしているのか分からないケースがあります。インテル® Inspector XE ではアプ リケーションの動作状況を表示させる機能があります。以下にその手順を記します。 まず、インテル® Inspector XE のプロジェクトプロパティーページを開いて「Advanced」グループ内にある 「Enable Collection progress information」にチェックがあることを確認します(前節の図を参照)。次にエ ラー検出を開始してしばらくすると下左図のように“Show details”という表記が表示されます。この表記の左 側にある印をクリックすると下右図のような画面が表示され、アプリケーションのスレッド動作内容が周期的 に更新されます。この更新が行われていれば、アプリケーションは動作中であることが分かります。また表示 内容には使用メモリー量も表示されています。 53 / 61 3.3.6 推奨されるコンパイルオプション インテル® Inspector XE の利用において、影響を与えるコンパイラーオプションを説明します。 オプション ・推奨オプション /Zi または /ZI 内容 ソースコードを参照するために、シンボル情報が必要となります。 /debug(リンカーオプション) 関数などのシンボル情報が必要となります。 ・影響を与えるオプション /Od 検出されたエラーをソースコード上で正しく表示することができます。最 適化されたバイナリーではソースコードを正しく表示できない場合があり ます。しかし最適化ありの Release 構成でビルドされたバイナリーに対し /MDd または /MD ・推奨されないオプション /RTC[su1] ても同様に診断を行うことが必要です。 特にスレッドエラーチェックの検出精度に影響を与えます。 メモリーエラーチェックの動作に影響を与えます。 このオプションは、インテル® Inspector XE の動作と似た内部処理を行うた め、”false positives” や “false negatives” の誤検出を招く可能性がありま す。 またこのオプションは未初期化メモリーを初期化してしまうのでインテル ® Inspector XE によるメモリー検出を阻害する場合もあります。 3.3.7 動 的 検 出 vs. 静 的 検 出 インテル® Inspector XE 2016 では、メモリー/スレッドエラーの動的検出をサポートしています。以前のバ ージョンでは、静的検出もサポートされていましたが、現在のバージョンで機能削除となりました。動的検出 は、プログラムを実行させながら検出処理を行う手法でインテル® Inspector XE の機能を使用します。静的検 出は、プログラムを実行せずに検出処理を行う手法です。両者の検出方法には、長所と短所がそれぞれ存在し ます。そのため、より確実なエラー検出を行うために両方の検出処理を実施しそれぞれの短所を補うことが望 まれます。 動的検出の長所としては、実行しないと分からない問題や静的検出では複雑すぎて検出不可能なケースでも検 出することができる点にあります。品質保証に役立ちますが、エラーを見つけ、そして修正することが第一目 的です。一方、静的検出の長所としては、コンパイル対象となるすべての処理が検出対象となる点です。動的 検出のように実際に実行されるパスだけでなく通常実行されないパスも対象となります。この点は脆弱性に対 する攻撃防止にも役立ちます。 54 / 61 3.4 イ ン テ ル ® VTune™ Amplifier XE 3.4.1 サンプリング・メカニズム インテル® VTune™ Amplifier XE では、以下の2種類のサンプリング手法が使用されます。 ① 「ユーザーモードサンプリングおよびトレースコレクション」 プロセッサーに周期的に割り込みを入れて、その割り込み処理の中で各種情報(インストラクション・ポ インターやスレッド情報など)を収集します。デフォルトでは 10ms に一回の割合で割り込みを入れてデ ータを収集します。その際のオーバーヘッドは約 5% 程度です。 本サンプリング手法を使用した解析タイプは、左図に示す 以下の 3 種類です。 ♦ ♦ ♦ ② Basic Hotspots… CPU 時間を多く消費する処理を特定する。 Concurrency … 並列性能を解析する。 Lock and Waits … スレッドをアイドル状態にする原因を特定する。 「イベント・ベース・サンプリング (EBS)」 インテル®プロセッサーに搭載される PMU(パフォーマンス・モニタリング・ユニット)のイベントカウ ンター・オーバーフローによる割り込みを利用してプロファイル情報を収集します。この割り込みの発生 回数がサンプリング回数となります。また割り込みの発生頻度は、PMU のオーバーフロー値を決定する “Sample After Value”を調整することで制御できます。例えば 1ms 単位で割り込みが発生した場合、それ によるオーバーヘッドは約 2% 程度となります。 注意:「イベントベース・サンプリングコレクション」は PMU を使用するため、このサンプリ ング手法を利用できるプロセッサーが限定されています。通常インテル® プロセッサー 上であれば動作します。詳細は製品リリースノートを参照してください。一方、「ユー ザーモードサンプリングおよびトレースコレクション」の場合はプロセッサーの制限を 受けません。 55 / 61 3.4.2 CPI 値 “Advanced Hotspots”解析では、主に CPI 値という指標データを表示します。 CPI (Clockticks Per Instructions retired)とは、実行完了命令数に対する消費 CPU クロック数を示しており、命 令実行の効率性を決定する基本的な指標です。1命令の実行に消費した CPU クロックを値で表しており、命 令の実行に消費するクロック数が小さいほど効率がいいと判断できます。 現在のインテル®プロセッサーでは、1 クロックで最大4つの命令を完了させることができます。つまり CPI の 最高理論値は“0.25”(=1/4)となります。ただし、これは理論値になるため、実際のアプリケーションでは CPI 値が 0.25 まで少なくなることはほとんどありません。 インテル® VTune™ Amplifier XE では、CPI 値が 1.00 より大きく、CPU 時間が全体の 5%より大きい関数に対 して赤色でマーキングし、チューニングの可能性があることを示します。 上図の例では、“multiply2”関数の CPI 値が赤色でマーキングされています。この CPI 値は EBS によって収集 された以下の2つのイベントから計算されています。 CPI 値 = CPU_CLK_UNHALTED.THREAD / INST_RETIRED.ANY ※ ※ CPU_CLK_UNHALTED.THREAD … CPU クロック数 INST_RETIRED.ANY … 実行完了命令数 このように CPI 値は、アプリケーション実行の効率性を測る基本指標となります。ただし、CPI 値は参考数値 であり目的は処理速度の向上(処理時間の減少)にあります。つまり CPU クロックのイベントである CPU_CLK_UNHALTED.THREAD の値を下げることが最終目標となります。 56 / 61 3.4.3 推奨されるコンパイルオプション インテル® VTune™ Amplifier XE は、基本的に全てのネイティブバイナリーに対してプロファイリングを行う ことができますが、ここでは、プロファイリングに影響を与えるコンパイラーオプションを説明します。 オプション ・推奨オプション /Zi /debug(リンカーオプション) /O2 など(“Release”ビルド) /MD または /MDd 内容 ソースコードを参照するために、シンボル情報が必要となります。 関数などのシンボル情報が必要となります。 より実践的なプロファイリングを行うことができます。 C ランタイム関数をユーザ・アプリケーションと区別することができま す。 ・TBB アプリケーションに推奨されるオプション インテル® VTune Amplifier XE が TBB 関数を正しく認識することがで /D"TBB_USE_THREADING_TOOLS" きます。 ・その他のオプション /Qopenmp (インテル®コンパイラー) 3.4.4 インテル® VTune Amplifier XE が OpenMP プラグマによる並列区間を特 定できるようにします。 特定箇所のプロファイル方法 インテル® VTune™ Amplifier XE は、基本的に Hotspot 関数を特定するプロファイラーです。プロファイル結 果は CPU 時間が多い順に表示されます。このためアプリケーションによっては、目的の関数が他の Hotspot 関数によって表示されないケースが発生します。この場合、Pause(ポーズ)機能と、Resume(再開)機能 を使用して、プロファイルする処理範囲を特定することで回避できます。また本機能を使用することで評価し たい処理に関する情報のみ表示させることが可能となり、結果内容を分析しやすくなります。 この機能の使用方法には、手動でプロファイル範囲を指定する方法と、インテル® VTune™ Amplifier XE が提 供する API を使用して明確にプロファイル範囲を指定する方法があります。 57 / 61 手動でこの機能を使用する場合は、GUI のボタンを使用してクリック操作を行います。 各種ボタンをクリックすることでサンプリングの開始・停止を制御することが可能です。 解析前に、アプリケーションの開始時からサンプリングを行うかどうか選択することができます。 アプリケーション実行時から解析を開始します。 解析を開始せずにアプリケーションを実行します。 解析中には、下記のボタン操作を行うことで、任意の処理をサンプリングすることが可能です。 中断している解析を開始します。 サンプリングとアプリケーションを終了し、解析結果を表示します。 サンプリングを一時的に中断させます。アプリケーションは中断しません。 サンプリングとアプリケーションを終了し、結果を破棄します。 ボタンをクリックしたタイミングで、解析結果の Timeline ペインにマークが表示します。 58 / 61 API を使用する手順について説明します。 1. プロファイルを行いたい処理に対してインテル® VTune™ Amplifier XE が提供する Resume 関数 (__itt_resume)と Pause 関数(__itt_pause)を追記します。 例えば以下のようにプロファイルする処理を Resume 関数と Pause 関数で挟みます。この API を使 用するにはヘッダーを宣言します。 #include “ittnotify.h” … Func() { 前処理 // プロファイル開始 __itt_pause(); // プロファイル停止 __itt_resume(); // プロファイル開始 __itt_pause(); // プロファイル停止 中間処理 処理 B 2. // プロファイルされない処理 __itt_resume(); 処理 A } // API 関数用ヘッダー 後処理 // プロファイルされる処理 // プロファイルされない処理 // プロファイルされる処理 // プロファイルされない処理 プロジェクトのプロパティページにヘッダーファイル(ittnotify.h)のディレクトリーパスを設定し ます。設定する内容は、デフォルトインストールを行った場合は以下のパスになります。 (x86 システム):C:¥Program Files¥IntelSWTools¥VTune Amplifier XE 2016¥include (x64 システム):C:¥Program Files (x86)¥IntelSWTools¥VTune Amplifier XE 2016¥include 59 / 61 3. ライブラリーの設定を行います。ライブラリーパスは、デフォルトインストールを行った場合は以下 の場所になります。 (x86 システム):C:¥Program Files¥IntelSWTools¥VTune Amplifier XE 2016¥lib32 (x64 システム):C:¥Program Files (x86)¥IntelSWTools¥VTune Amplifier XE 2016¥lib32 (※解析対象が Intel64 アプリケーションの場合は¥lib32 ではなく¥lib64 を指定してください) 指定ライブラリー:libittnotify.lib 4. 設定が完了したら、プロジェクトをビルドし直します。 5. ビルドが正常に完了したら、[Start Paused] ボタンをクリックして解析停止状態でアプリケーション を実行します。アプリケーションが Resume 関数を実行したタイミングでプロファイルが開始され ます。その後は、Pause 関数、Resume 関数の配置によってプロファイルが行われます。 4 追加情報 4.1 N-Queens サ ン プ ル ・ プ ロ グ ラ ム 本ドキュメントでは、N-Queens プロジェクト内の 1_nqeens_serial プロジェクトを使用し OpenMP による マルチスレッド方法を説明しましたが、N-Queens プロジェクトにはその他のマルチスレッド手法で記述され たプロジェクトが含まれています。 ① シンプル・シリアル・ソリューション ② OpenMP を使用したパラレル・ソリューション ③ Intel® Cilk Plus を使用したパラレル・ソリューション ④ Intel® TBB を使用したパラレル・ソリューション 60 / 61 4.2 OpenMP に つ い て OpenMP に関する参考資料および参考サイトをご紹介します。 『インテル® コンパイラー OpenMP* 入門』 http://jp.xlsoft.com/documents/intel/compiler/525J-001.pdf 『インテル® コンパイラー OpenMP* 活用ガイド』 http://jp.xlsoft.com/documents/intel/compiler/526J-001.pdf(C言語ユーザー用) http://jp.xlsoft.com/documents/intel/compiler/527J-001.pdf(Fortran 言語ユーザー用) ・OpenMP ホームページ http://openmp.org/wp/ 4.3 Intel® Cilk™ Plus に つ い て Intel® Cilk™ Plus に関する参考資料および参考サイトをご紹介します。 ・Intel® Cilk™ Plus ホームページ http://software.intel.com/en-us/intel-cilk-plus 4.4 Intel® TBB に つ い て TBB に関する参考資料および参考サイトをご紹介します。 ・TBB ホームページ http://threadingbuildingblocks.org/ ・TBB 製品紹介サイト http://www.xlsoft.com/jp/products/intel/perflib/tbb/index.html http://software.intel.com/en-us/intel-tbb 5 最後に 本製品に関するその他の情報は以下のサイトをご参照ください。 http://xlsoft.com/jp/products/intel/studio_xe/index.html 本製品に関してご不明な点がありましたら、下記お問い合わせ窓口より弊社サポートまでご連絡ください。 https://www.xlsoft.com/jp/services/xlsoft_form.html 最適化・並列化全般の技術情報発信サイト: http://www.isus.jp/ 61 / 61