はじめに
マルチコアプロセッサーの急速な普及により、消費者と開発者の両方にとって新しい時代が到来しました。消費者にさまざまな可能性が提供される一方で、開発者は新しいマルチコアプロセッサーの優れた処理能力を活用するソフトウェアを開発する必要性に迫られています。
インテルは、マルチコアシステムを活用するためのツール提供を使命としています。その取り組みの一環として、Microsoft Visual Studio C/C++を使用する開発者向けにインテルParallel Studioを発表し、幅広い分野のソフトウェア開発者にParallel Universeへの扉を開きました。ここでは、インテルParallel Studioに含まれる各ツールを紹介します。
並列化の各フェーズを支援するツール
インテルParallel Studioは、コンパイル/デバッグ、エラー検証、パフォーマンス分析といった並列化の各フェーズを支援するツールで構成されています。インテルParallel Composerでは、コンパイラー/デバッガーとスレッド化ライブラリーによって、ソフトウェア開発における並列化の導入を迅速に行うことができます。インテルParallel Inspectorは、事前に「バグを発見」するエラー検出ツールです。並列化プログラミングモデルを問わず、データ競合やデッドロックのような発見が困難なスレッド化エラーを検出し、アプリケーションの信頼性を向上する柔軟なツールです。インテルParallel Amplifierは、スケーリングを制限してしまう予期し難い直列化の発見に役立つパフォーマンス分析ツールです。マルチコアプロセッサー向けに最適なパフォーマンスを発揮する並列アプリケーションのためのきめ細かな調整を行うことができます。
インテルParallel Composer
インテルParallel Composerは、並列処理の迅速な導入を支援するコンパイラー、デバッガー、ライブラリーを提供します。広範囲の並列プログラミングモデルがサポートされているため、開発者は開発アプリケーションに最も適したコーディング方法を見つけることができます。
インテルC++コンパイラー
インテルC++コンパイラーはMicrosoft VisualStudio環境に統合され、OpenMP 3.0、ラムダ関数、自動ベクトル化、自動並列化、スレッド化ライブラリーのサポートなど、アプリケーションレベルで並列化を行うための次の拡張機能を提供します。
- ベクトル化サポート
インテル コンパイラーのコンポーネントである自動ベクトライザーは、MMX命令、インテル ストリーミングSIMD拡張命令(インテルSSE、SSE2、SSE3およびSSE4べクトル化コンパイラー命令およびメディアアクセラレータ命令)とストリーミングSIMD拡張命令3補足命令 (SSSE3)を自動的に使用します。
- OpenMP 3.0サポート
インテル コンパイラーは、開発者がソースプログラムで指定したOpenMP宣言子にしたがってコード変換を実行し、マルチスレッドコードを生成します。生成されるコードは、単一プロセッサーシステムおよびマルチプロセッサーシステムの両方で並列実行が可能です。また、OpenMPバージョン3.0仕様に、ランタイムライブラリールーチンおよび環境変数を含むインテル独自の拡張機能を提供します。
- 自動並列化機能
インテル コンパイラーの自動並列化機能は、入力プログラムの直列部分を同等のマルチスレッドコードに自動的に変換します。自動並列化機能は、ワークシェアリング候補のループを特定し、正しい並列実行を確認するためにデータフロー解析を行います。
- C++ラムダ関数サポート
C++ラムダ式は、関数オブジェクトを定義する基本式です。このような式は関数オブジェクトが想定される場所ではどこでも使用できます( 例:Standard TemplateLibrary(STL)アルゴリズムの引数)。インテルC++コンパイラーは、ISO C++ドキュメントで定められているラムダ式を実装しています。/Qstd=c++0xスイッチを使用すると、ラムダサポートが有効になります。
- valarray
ハイパフォーマンスコンピューティング向けの配列演算を含む配列のC++ STL(標準テンプレートライブラリー)コンテナクラスです。これらの演算はベクトル化などのハードウェア機能を活用します。インテル コンパイラーはvalarrayを組み込み型として認識し、インテルIPPライブラリーの呼び出しに置換します。
表1 インテルC++ コンパイラーが提供する代表的なコンパイルオプション
/O1 | サイズを最適化。オブジェクトのサイズを増やす傾向がある最適化を省略。多くの場合、最小限のサイズで最適化されたコードが作成される。 |
/O2 | 最速化。ベクトル化を含む多くの最適化を有効にし、多くの場合、/O1よりも速いコードを作成する。 |
/O3 | /O2の最適化に加えて、スカラー置換、ループアンロール、分岐を除去するコード反復、より効率的にキャッシュを使用するループブロッキングなど、強力なループの最適化およびメモリーアクセスの最適化を行う。 |
/Qopenmp | OpenMP宣言子がある場合、マルチスレッドコードを生成する処理をパラレライザーに指示。スタックのサイズを増やさなければならないことがある。 |
/Qparallel | 安全に並列実行可能な単純構造のループを検出し、そのループに対するマルチスレッドコードを自動的に生成する。 |
/Qax | 自動プロセッサーディスパッチを行う。対象となるインテルプロセッサー向けに専用のコードを生成し、またデフォルトのコードも生成する。カンマで区切って複数の値を指定し、同一ファイルで追加のプロセッサー向けのチューニングを行うことができる。 |
インテルParallel Debugger Extension
インテルParallel Debugger Extensionは並列コード開発機能向けのアドオンデバッガーであり、Microsoft Visual Studioに統合し、次の拡張機能を提供します。
- 異なるスレッドからの同一データ要素へのアクセスを検出するスレッドデータ共有解析
- 再入可能な関数呼び出しでプログラムの実行を停止するスマートブレークポイント機能
- OpenMPの並列ループで動的な追加作業スレッドの作成を有効/無効にするシリアル実行モード
- 高度なOpenMPプログラム状態解析用のOpenMPランタイム情報ビュー
- SIMD(Single Instruction Multiple Data)命令セットを使用して並列データをデバッグするように、形式オプションと編集オプションが拡張されたストリーミングSIMD拡張命令(SSE)レジスタービュー
インテルParallel Debugger Extensionは、ソースインストルメンテーションを使用してデータ共有問題を特定します。この機能を有効にするには、Visual Studio環境から[構成プロパティ]-[C/C++]-[General(全般)]-[Debug(デバッグ)]-[Enable Parallel Debug Checks(並列デバッグ検証を有効にする)]を選択して、/debug:parallelを設定します。
図2はインテルParallel Debugger Extensionが同じデータにアクセスする2つのスレッドを検出して、アプリケーションの実行を停止したことを示しています。
並列化を支援するライブラリー
インテルParallel ComposerはC/C++において使いやすく、ハイパフォーマンスで移植性の高い並列アプリケーションの開発を支援する次の2つのライブラリーを提供します。
- インテルTBB
受賞歴もあるテンプレートベースのランタイムライブラリーです。開発者は並行収集と並列アルゴリズムを活用して、マルチコアプロセッサーの潜在的なパフォーマンスを活用するスケーラブルなアプリケーションを作成することができます。
- インテルIPP
マルチメディア、データ処理、通信アプリケーション向けに高度に最適化された、マルチコア対応ライブラリーです。信号処理、オーディオコーディング、音声認識、音声コーディング、画像処理、ビデオコーディング、小行列の演算、3Dデータ処理などの分野でアプリケーションを作成するための基本的な低レベルの関数が幅広く用意されています。
インテルParallel Inspector
マルチスレッドアプリケーションでエラーの原因を特定することは困難です。インテルParallel Inspectorは、C/C++アプリケーション開発者に次の分析機能を提供し、スレッド化特有のエラーやメモリエラーといったバグを発見し、原因の特定を支援します。
- リーク、バッファーオーバーランエラー、ポインタの問題など、メモリとリソースに関するさまざまな問題を特定
- スレッド関連のデッドロック、データ競合、およびその他の同期化問題を予測し特定
- 並列アプリケーションの潜在的なセキュリティ問題の特定
- サイズ、頻度、種類順でエラーをすばやくソートし、重大な問題を認識して優先順位を設定
インテルParallel Inspector(図3)は、Pinと呼ばれる動的なバイナリインストルメンテーションテクノロジを使用して、メモリエラーとスレッド化エラーを検証します。実行中の実行ファイルの任意の場所にC/C++コードを挿入して、プログラムの動作を監視できます。
インテルParallel Inspectorのメモリ分析
インテルParallel Inspectorのメモリ分析では、Pinの設定を変更して4つのレベルで分析を行うことができます(図4)。
- レベル1分析
プリケーションのメモリリークを見つけるのに役立ちます。メモリリークは、メモリブロックが割り当てられ、解放されない場合に発生します。
- レベル2分析
初期化されていないメモリへのアクセス、不正な割り当て解除、割り当てと解除の不一致など、アプリケーションの不正なメモリアクセスを検出します。
- レベル3分析
レベル2分析に加え、コールスタックの深さが1から12に変更され、ぶら下がりポインタの拡張チェックが有効です。ぶら下がりポインタとは、すでに存在しないデータにアクセスしたり、そのようなデータを指定しているポインタです。
- レベル4分析
すべてのメモリエラーを見つけようとします。コールスタックの深さは32に変更され、ぶら下がりポインタの拡張チェック、システムライブラリーの分析、およびスタック上で発生するメモリエラーの分析も行います。
図5に示すように、検出されたメモリエラーを重大度、説明、原因、関数名などでフィルタできます。
インテルParallel Inspectorのスレッド化エラー分析
スレッド化エラーの分析レベルも4つあります(図6)。インテルParallel Inspectorで認識される主なスレッド化エラーには、データ競合、デッドロック、ロック階層違反、および潜在的なプライバシーの侵害があります。
- レベル1分析
アプリケーションのデッドロックを見つけるのに役立ちます。デッドロックは、2つ以上のスレッドが、他のスレッドがリソース(MUTEX、クリティカルセクション、スレッドハンドルなど)を解放するのを待機しているのに、どのスレッドもリソースを解放しない場合に発生します。コールスタックの深さは1に設定されます。
- レベル2分析
アプリケーションのデータ競合とデッドロックを検出します。データ競合は最も一般的なスレッド化エラーの1つで、適切な同期処理が行われずに複数のスレッドが同じメモリ位置にアクセスした場合に発生します。このレベルでも、コールスタックの深さは1に設定されます。このレベルのバイトレベルの粒度は4です。
- レベル3分析
レベル2のデータ競合とデッドロックを検出に加えて、それらの発生場所も検出します。より詳細な分析を行うために、コールスタックの深さは12に設定されます。このレベルのバイトレベルの粒度は1です。
- レベル4分析
すべてのスレッド化エラーを見つけようとします。コールスタックの深さは32に変更され、スタック上で発生するエラーの分析も行います。スタック分析はこのレベルでのみ有効になります。このレベルのバイトレベルの粒度は1です。
データ競合はさまざまな場合に発生します。インテルParallel Inspectorは、write-write(書き込み-書き込み)、read-write(読み取り-書き込み)、およびwrite-read(書き込み-読み取り)データ競合を検出します。すべての場合において、実行順序が共有データに影響を与えます。
- write-writeデータ競合:2つ以上のスレッドが同じメモリ位置に書き込みを行う場合に発生
- read-writeデータ競合:1つのスレッドがメモリ位置から読み取りを行う際に、別のスレッドが同時に同じメモリ位置に書き込みを行う場合に発生
- write-readデータ競合:1つのスレッドがメモリ位置に書き込みを行う際に、別のスレッドが同時に同じメモリ位置から読み取りを行う場合に発生
インテルParallel Amplifer
マルチスレッドアプリケーション特有のパフォーマンス問題は、次のように要約できます。
- 複雑性(データ再構成、同期化の使用)
- パフォーマンス(最適化とチューニングの必要性)
- 同期オーバーヘッド
インテルParallel Amplifier(図8)は、開発者が投資収益率(ROI)の最も高い最適化を実現できるように、コードのボトルネックを特定するのを支援します。アプリケーションのパフォーマンス問題を特定し適切に排除することは、効率的な最適化を行ううえで重要です。
インテルParallel Amplifierでは数回のマウスクリック操作を行うだけで、hotspot分析(図9)、並列性分析(図10)、そしてロックと待機の分析という3種類の強力なパフォーマンス解析を行うことができます。なお、各分析で使用する指標は次のとおりです。
- 経過時間:アプリケーションの実行時間
- CPU時間:プロセッサーでスレッドが実行に消費した時間。複数のスレッドの場合は、すべてのスレッドのCPU時間が合計される
- 待機時間:指定されたスレッドが、同期待機やI/O待機などのイベントが発生するまで待機に要した時間
hotspot分析
オーバーヘッドの低い統計サンプリング(スタックサンプリングとも呼ばれる)アルゴリズムを使用することで、開発者がアプリケーションフローを理解し、実行に時間のかかるコードセクション(hotspot)を特定できるようにします。hotspot分析中、インテルParallel AmplifierはOSタイマーを使用して一定の間隔でサンプリングを行い、アプリケーションをプロファイルします。
すべてのアクティブな命令アドレスとその呼び出しシーケンスのサンプルが収集されます。そして、格納されたサンプリング結果のインストラクションポインタ(IP)と関連する呼び出しシーケンスを分析し、表示します。統計的に収集されたIPサンプルと呼び出しシーケンスを使用して、インテルParallel Amplifierはコールツリーを生成し、表示します。
並列性分析
並列性分析は、アプリケーションがシステムで利用可能なプロセッサーをどれだけ活用しているかを測定します。図10に示すように、プロセッサーが有効に利用されていないhotspot関数を特定するのに役立ちます。
並列性分析中、インテルParallel Amplifierはアクティブなスレッド数を収集して提供します。並列レベルとプロセッサー数を比較することで、アプリケーションによるシステムで利用可能なプロセッサーの利用状況を分類します。
「Concurrency(並列性)」ウィンドウと「Locks and Waits(ロックと待機)」ウィンドウの時間は、次の利用状況を示します(図11)。
- Idle:プログラム中のすべてのスレッドが待機中。実行中のスレッドはない
- Poor:十分に利用されていない。デフォルトではスレッド数が並列化ターゲットの50%以下の場合
- OK:許容可能範囲で利用されている。デフォルトではスレッド数が並列化ターゲットの51.85%の場合
- Ideal:理想的に有効利用されている。デフォルトではスレッド数が並列化ターゲットの86.115%の場合
ロックと待機の分析
並列性分析がアプリケーションの並列化されていない場所や利用可能なプロセッサーを十分に活用していない場所の特定を支援する一方で、ロックと待機の分析は、プロセッサーが有効に利用されていない原因を特定するのに役立ちます(図12)。プロセッサーが有効に利用されていない最も一般的な原因は、スレッドが同期オブジェクト(ロック)を長時間待機しているためです。多くの場合、この間に有効な作業が行われないため、パフォーマンスが低下し、プロセッサーが有効に使用されません。
ロックと待機の分析中、開発者は各同期オブジェクトの影響を予測することができます。分析結果は、各同期オブジェクトまたはスリープやブロッキングI/OなどのブロッキングAPIにおける、アプリケーションの待機時間を理解するのに役立ちます。
インテルParallel InspectorとインテルParallelAmplifierは、両方ともソースコードレベルまでドリルダウンすることができます。これは非常に重要なことです。たとえば、図13の行をダブルクリックしてソースコードにドリルダウンし、問題を引き起こしている同期オブジェクトを確認できます。
まとめ
並列プログラミングは新しいものではありません。長年、ハイパフォーマンスコンピューティング(HPC)の分野でよく研究され、使用されてきました。今日、マルチコアプロセッサーの普及に伴い、並列プログラミングは主流となりつつあります。今こそ、まさにインテルParallel Studioの出番と言えるでしょう。
インテルParallel Studioはソフトウェア開発者が並列化プログラミングへ挑戦する機会をもたらすツールであり、ソフトウェア開発者が並列処理の世界に足を踏み入れる際の障壁を劇的に減らします。まだ試したことのない方はぜひインテルParallel Studioの30日無償評価版をインストールして、並列化と支援ツールの可能性をお確かめください。
- お問い合わせ先
エクセルソフト(株)