OpemMPに触れてみた。

OpenMP
http://openmp.org


並列処理が簡単に出来るOpemMPを今更試してみた。


手元の環境はWindowsXP + GCC4.5.0(g++)
GCCなら4.2.0以降であれば、特に何もしないでも-fopenmpをつけるだけでOpemMPが使えるらしい。

openmp_example.cpp
#include <iostream>
#include <ctime>
	
int main(){
	const unsigned int loop = 4000000000U;
	clock_t start,end;
	start = clock();
	#ifdef _OPENMP
	#pragma omp parallel
	#endif
	{
		#ifdef _OPENMP
		#pragma omp for
		#endif
		for(unsigned int i=0; i<loop; ++i){
			;
		}
	}
	end = clock();
	
	std::cout << (end-start) << "clock" << std::endl;
	return 0;
}
結果

g++ -Wall -o openmp_example.exe openmp_example.cpp

12234clock

ファイルサイズ 25,899 Byte
メモリ使用量1,364KB, スレッド数:1

g++ -Wall -fopenmp -o openmp_example.exe openmp_example.cpp

3015clock

ファイルサイズ 27,320Byte
メモリ使用量1,848KB, スレッド数:4
libgomp-1.dllが必要


4倍近く早くなっているので、並列化が有効になってるのがわかる。


並列処理の弊害

複数のスレッドが一つの変数にアクセスし、値を更新する場合、注意が必要。

#include <iostream>
#include <ctime>
	
int main(){
	const unsigned int loop = 100000000U;
	unsigned int count = 0;
	clock_t start,end;
	start = clock();
	#ifdef _OPENMP
	#pragma omp parallel
	#endif
	{
		#ifdef _OPENMP
		#pragma omp for
		#endif
		for(unsigned int i=0; i<loop; ++i){
			count++;
		}
	}
	end = clock();
	
	std::cout << (end-start) << "clock" << std::endl;
	std::cout << "count:" << count << std::endl;
	return 0;
}

640clock
count:52728384

countの値がloopと等しくなるはずだが、並列処理によりcountの値が正しく更新できていない。
並列処理中にアトミックな処理が必要な場合、omp atomicプラグマを指定する。

			#ifdef _OPENMP
			#pragma omp atomic
			#endif
			count++;

7422clock
count:100000000

なぜかブロックで括ることができないので、複数行にわたって書く場合、いちいち#pragma omp atomicを書く必要がある。
#pragma omp criticalもあるが、atomicとは違うので注意。


いちいち#ifdefで確認しなくても、サポートしていないプラグマならだいたい読み飛ばされるだけなので、面倒な人は書かなくてもいいかもしれない。
GCCの場合、-Wallでunknownなpragmaに対して警告が出るようになっていても、
"-Wno-unknown-pragmas"を加えるとこの警告だけが無効になる。