OpenMP 3.0のサンプル
インテル C++ コンパイラー 11.1は最新のOpenMP 3.0に対応しています。OpenMP3.0に興味があった筆者は早速試してみました。OpenMP 3.0では「タスク」の概念が追加され、従来並列化できなかった複雑なコードも並列化することができます。
このサンプルの内容は、終了条件が事前に明確でないループを並列化できることを示しています。これは、#pragma omp forではできない並列化です。なお、このサンプルはあくまでもタスクを試すことを目的としたものであり深い意味はありません。数値を合計しているのは、正しく並列化できていることを確認するためのものです。
#include <cstdio>
#include <climits>
#include <ctime>
#include <iostream>
#include <windows.h>
#include <winnt.h>
#include <tchar.h>
#include <process.h>
#include <omp.h>
using namespace std;
int main()
{
//ロケールを設定してコンソールで日本語表示が出来るようにする
_tsetlocale( LC_ALL, _T("") );
//最大値が出るまでランダムに数を生成
srand( static_cast<unsigned int> ( time( NULL ) ) ); //ランダムに生成
const int maxCount = 100; //最大数
const int maxNumber = 100;
const int magicNumber = maxNumber << 1; //終了を示す値
const int processingTime = 10; //処理時間(ミリ秒単位)
int* numbers = new int[ maxCount ];
for ( int i = 0; i < maxCount; i++ ) {
int number = static_cast<int>( ( rand() % maxNumber ) + 1 );
if ( number == maxNumber )
{
//ここで終了
numbers[ i ] = magicNumber;
break;
} else {
numbers[ i ] = number;
}
}
numbers[ maxCount - 1 ] = magicNumber; //最後まで到達した場合に備える
int* numbers1 = new int[ maxCount ];
for ( int i = 0; i < maxCount; i++ ) {
int number = static_cast<int>( ( rand() % maxNumber ) + 1);
if ( number == maxNumber )
{
//ここで終了
numbers1[ i ] = magicNumber;
break;
} else {
numbers1[ i ] = number;
}
}
numbers1[ maxCount - 1 ] = magicNumber; //最後まで到達した場合に備える
//逐次的に総計を算出
long int sum = 0;
double start = omp_get_wtime();
int index = 0;
while ( true ) {
if ( numbers[ index ] == magicNumber ) break;
sum += numbers[ index ];
++index;
Sleep( processingTime );
}
int saveIndex = index;
index = 0;
while ( true ) {
if ( numbers1[ index ] == magicNumber ) break;
sum += numbers1[ index ];
++index;
Sleep( processingTime );
}
int saveIndex1 = index;
double end = omp_get_wtime();
double second = end - start;
cout << "逐次的に計算した場合" << second << "秒かかりました。" << endl;
//並列的に総計を算出
long int ompSum = 0;
long int ompSum1 = 0;
start = omp_get_wtime();
#pragma omp parallel
{
#pragma omp single nowait //これがないと同じ処理をスレッド数と同じだけ行ってしまう
{
index = 0;
#pragma omp task firstprivate(index)
{
while ( true ) {
if ( numbers[ index ] == magicNumber ) break;
ompSum += numbers[ index ];
++index;
Sleep( processingTime );
}
}
index = 0;
#pragma omp task firstprivate(index)
{
while ( true ) {
if ( numbers1[ index ]== magicNumber ) break;
ompSum1 += numbers1[ index ];
++index;
Sleep( processingTime );
}
}
}
}
ompSum += ompSum1;
end = omp_get_wtime();
double ompSecond = end - start;
cout << "並列的に計算した場合" << ompSecond << "秒かかりました。" << endl;
//処理結果をチェック
if ( sum != ompSum )
{
cout << "並列処理に誤りがあります" << endl;
cout << "直列:" << sum << " 並列:" << ompSum << ":" << ompSum1 << endl;
}
cout << "処理量が" << saveIndex << "と" << saveIndex1 << "の場合";
cout << "処理効率は" << ( second / ompSecond ) << "倍です。" << endl;
//後始末
delete[] numbers;
delete[] numbers1;
}
まずプロジェクトで右クリックして[インテル C++コンパイラー・プロフェッショナル]-[インテル C++を使用]をクリックしてコンパイラーを切り替えてください。そして[プロパティ]-[C++]-[言語]を選択してから「OpenMPのサポート」を並列コードの生成(/openmp, /Qopenmp と同等)にして下さい。そして、最後にデバッグ実行して下さい。OpenMP 3.0の効果が目視できます。
何度か実行して試して下さい。データ処理量により効率が変動します。この結果から処理を並列化する際には、適用する部分をよく考える必要があることが分かります。

まとめ
今回の記事では、大量のリソースを効果的に利用するための手段が必要であることと、インテル C++ コンパイラー 11.1がその目的を達成するだけの機能を備えていることを紹介しました。
インテル C++ コンパイラー 11.1の機能は多く、今回紹介しきれませんでしたが、OpenMPによる並列化ソースコードを分析し、メモリーリーク、デッドロックなどの問題を診断する「Parallel Lint」、リビルドしなくてもデバッグセッション中にシリアルモードで並列領域をステップ実行できる「インテル Parallel Debugger Extension」などの魅力的な機能がまだまだあります。
エクセルソフト社のホームページで評価版を入手できますので、皆様はぜひご自身の手でその豊富な機能をお試しください。日本語に対応していますので容易に、次世代の技術を思う存分味わうことができるでしょう。

