C++ Composer XEの新機能
C++ Composer XEは、出力するコードの高速性で定評のあるインテルC++コンパイラーと、並列化アプリケーションのためのライブラリで構成されています。含まれるライブラリは表3のとおりです。
ライブラリ | 概要 |
インテル MKL 10.3(Math Kernel Library) | 工学、科学、金融系アプリケーションなどで活用できるマルチスレッド算術ライブラリ |
インテル IPP 7.0(Integrated Performance Primitives) | マルチメディア処理(画像や動画)、信号処理(音声など)、データ処理(データ圧縮や暗号化など)のライブラリ |
インテル TBB 3.0(Threading Building Blocks) | スレッドをタスクに抽象化し、スケーラブルな並列処理を行うためのライブラリ |
今回のバージョンでは、前バージョン(インテルC/C++コンパイラー プロフェッショナル エディション)から以下のような新機能が追加されています。
- 次期標準であるC++0xのサポートを拡大
- 並列化のためのC/C++言語拡張であるインテル Cilk Plusのサポート
- ガイド付き自動並列化
- 最新プロセッサのサポート
ここではガイド付き自動並列化機能について解説します。
なお、Cilk Plusについては、先に触れたParallel Studio 2011の記事で詳しく解説していますので参照してください。
ガイド付き自動並列化
ガイド付き自動並列化とは、インテル C++コンパイラーの新機能で、逐次処理で記述したアプリケーションを解析し、並列化のためのアドバイスを生成する機能です。もちろんコンパイラーがソースコードを自動的に修正するわけではなく、「インテル C++コンパイラーの持っている並列化処理機能を有効に使用するためにはソースコードのどの部分を修正する必要があるか」を教えてくれる機能です。
例えば、リスト1は製品付属のサンプルで、浮動小数点の配列について演算を行うプログラムの一部です({Parallel Studio XE 2011をインストールしたフォルダ}\Composer\Samples\en_US\C++\GuidedAutoParallel.zip)。
このサンプルでは浮動小数点の配列Aをループし、条件に合わせて浮動小数点演算を行います。インテル C++コンパイラーの持つベクトル化(配列をループして1つずつ処理するのではなく、同時に処理する)機能を使えば、高速化を図ることができそうです。
1: void test_scalar_dep(double *A, int n) 2: { 3: int i; 4: double b; 5: for (i=0; i<n; i++) { //配列内をループ 6: if (A[i] > 0) {b=A[i]; A[i] = 1 / A[i]; } //bは条件付きで代入 7: if (A[i] > 1) {A[i] += b;} 8: } //変数bの値が、次のループまで持ち越される 9: }
このプログラムについてガイド付き自動並列化を行うと、インテル C++コンパイラーからリスト2のようなガイドメッセージが出力されます。メッセージが英語なので分かりづらいですが、「リスト1の6行目で変数bへの条件付き代入があるため、ループをベクトル化できない」という内容です。
1>scalar_dep.cpp(42): warning #30521: (PAR) Assign a value to the variable(s) "b" at the beginning of the body of the loop in line 42. This will allow the loop to be parallelized. [VERIFY] Make sure that, in the original program, the variable(s) "b" read in any iteration of the loop has been defined earlier in the same iteration.
今回はbの値が前のループの処理に依存するため、nループ目とn+1ループ目を同時に処理することができません。
従って、ループをベクトル化するためには、変数bへの代入をループの最初に移動し、条件なし代入を行う必要がある、ということになります。もちろんこの変更により、処理内容は変わりますので、修正して正しい結果が得られるのかどうかはきちんと検討する必要があります。
今回の場合はリスト3のように変数bへの代入文をループの先頭に移動させることで、並列化の条件を満たすことができました。
for (i=0; i<n; i++) { b=A[i]; //ループの先頭で変数bに条件なし代入 if (A[i] > 0) { A[i] = 1 / A[i]; } if (A[i] > 1) { A[i] += b;} }
これにより、ループは前のループに依存することがなくなったため、インテルC++コンパイラーのベクトル化機能で並列処理を行うことができます。今回の例では、この修正により、表4のように3倍以上の処理速度の向上を確認できました。
処理時間(ミリ秒) | 処理速度(GFlops) | |
最適化前 | 8016 | 0.2495 |
最適化後 | 2332 | 0.8576 |
このようにC++ Composer XEでは、コンパイラーが出力した並列化に関するガイドを参考に、ソースコードを修正して並列化を行うことができます。