書籍転載
文法からはじめるプログラミング言語Microsoft Visual C++入門

並列処理を行うための基礎知識(Visual C++)
――第13章 並列処理〜マルチスレッドプログラミング(後編)――

WINGSプロジェクト 矢吹 太朗(監修 山田 祥寛)
2010/04/28
Page1 Page2 Page3 Page4

13.5 OpenMP

 並列化のためのAPIであるOpenMPを利用して、素数を列挙するプログラムを並列化します。OpenMPを使うと、先に紹介した2つの方法(BoostのthreadとSystem::Threading::Thread)と比べて、とても簡単に並列化を実現できます。

13.5.1 OpenMPの準備

 OpenMPは並列処理のための標準的なAPIです。Visual C++だけでなく、GCCやIntelのC++コンパイラなど、多くのコンパイラでサポートされています。詳細については、MSDNライブラリの「Visual C++のOpenMP」や、OpenMP.orgで公開されている規格書の日本語訳などを参照してください*4

*4 規格書のバージョンは3.0ですが、Visual C++ 2008がサポートしているOpenMP のバージョンは2.0なので、規格書のすべてが実装されているわけではありません。

 Visual C++ Express EditionでOpenMPを使うためには、次の2つの準備が必要です。

  1. プロジェクトのプロパティでOpenMPを有効にする([構成プロパティ]の[C/C++]、[言語])(図13-6)
  2. vcomp90.dllをPATHの通ったディレクトリにコピーする

 このファイルは[c:\Windows\winsxs\x86_Microsoft.VC90.OpenMP_...]フォルダにありますが、見つからない場合には、Microsoft Visual C++ 2008 SP1再頒布可能パッケージ(x86)をインストールしてください。

図13-6 プロジェクトのオプションでOpenMPを有効にする

13.5.2 OpenMPの利用

 OpenMPを利用する際には、スレッドのことを考える必要はほとんどありません。並列化したい部分を指定するだけで十分です。素数を列挙する問題のための逐次型のプログラム(13.2.2項の13-primes-single.cpp)は、次のように修正することで並列化されます。

//このコードはReleaseモードでビルドする
#include <vector>
#include <ctime>
#include "number.h"
#include <algorithm> //修正点1
using namespace std;

const int N=100000;

int main()
{
  clock_t start=clock();

  vector<int> primes;

  #pragma omp parallel //修正点2
  {
    #pragma omp for schedule(dynamic, 1000) //修正点3
    for (int n=2; n<=N; ++n) {
      if (isPrime(n)) {
        #pragma omp critical //修正点4
        primes.push_back(n);
      }
    }
  }

  cout<<"There are "<<primes.size()<<" prime numbers.\n";
  sort(primes.begin(), primes.end()); //修正点5
  report(primes.begin(), primes.end());

  clock_t finish=clock();
  double duration=double(finish-start)/CLOCKS_PER_SEC;
  cout<<"Elapsed time: "<<duration<<" sec.\n";
}
[サンプル]13-primes-openmp.cpp

 13.2.2項の13-primes-single.cppに施した修正は、次の5点です。

  1. <algorithm>の読み込み
      これは関数sort()を利用するためです。

  2. #pragma omp parallelブロック
      このブロックの中が並列処理されることになります*5

  3. #pragma omp for schedule(dynamic, 1000)
      この記述の直後に書いたfor文が並列処理されます。引数の1000はスレッドに分割する単位です。素数かどうかを調べる数を1000個単位でスレッドに渡しています。

  4. #pragma omp critical
      この記述の次に書く文が排他制御の対象になります

  5. sort(primes.begin(), primes.end());
      素数を列挙子終わった直後のベクタの要素は順番がバラバラになっているため並べ替える必要があります。

*5 利用できるCPU の数だけスレッドが生成されます。#pragma omp parallel num_threads( スレッド数) として、スレッド数を指定することもできます。利用しているスレッドの数は、関数omp_get_num_threads() で取得できます(<omp.h> が必要です)。

 プログラムの実行結果は次のとおりです。シングルスレッドの場合(約9.4秒)と同様の結果が、約4.8秒で得られました。

There are 9592 prime numbers.
2 3 5 7 11 13 17 19 23 29
99877 99881 99901 99907 99923 99929 99961 99971 99989 99991
Elapsed time: 4.782 sec.

 #pragmaで始まるOpenMPのための指示文(ディレクティブ)のうち、使用頻度が高いと思われるものを次の表にまとめました。

ディレクティブ 機能
#pragma omp atomic 次の行の複合代入文を不可分に実行する
#pragma omp barrier 全スレッドを同期する
#pragma omp critical 次の行(あるいはブロック)を排他的に実行する
#pragma omp for 次のfor文を分割して実行する
#pragma omp master 次の行(あるいはブロック)を0番目のスレッドで実行する
#pragma omp parallel 次の行(あるいはブロック)を複数スレッドで実行する
#pragma omp section sectionsブロック内で、並列に実行する文を指定する
#pragma omp sections 次のブロック中で、section指示文で指示される文をそれぞれ並列実行する
#pragma omp single 全スレッドを同期してから次の行(あるいはブロック)を単一スレッドで実行する
#pragma omp threadprivate(var) 変数varを各スレッドのプライベート変数にする
OpenMPディレクティブ

 OpenMPにはディレクティブの他に関数も用意されています(次の表)。この節では利用していませんが、スレッドごとに異なる処理をさせたい場合などに、これらの関数が役立ちます。

関数プロトタイプ 機能
int omp_get_dynamic(); スレッド数の動的制御が有効かどうかを返す(0以外なら有効)
void omp_set_dynamics(int); スレッド数の動的制御の有効/無効を設定する(引数が0なら無効)
int omp_get_max_threads(); 最大のスレッド数を返す
int omp_get_nested(); 並列実行領域のネストが有効かどうかを返す(0以外なら有効)
void omp_set_nested(int); 並列実行領域のネストの有効/無効を設定する(引数が0なら無効)
int omp_get_num_procs(); プログラムで使用可能なプロセッサ数を返す
int omp_get_num_threads(); 並列実行領域のスレッド数を返す
int omp_set_num_threads(int); 並列実行領域で使用するスレッド数を設定する
int omp_get_thread_num(); 並列スレッドの番号を返す
int omp_in_parallel(); 並列実行中かどうかを返す(0以外なら並列実行中)
OpenMPの関数(<omp.h>)

 次回からは3回に渡り、標準C++のクラスおよびオブジェクト指向プログラミングを解説します。End of Article

 

 INDEX
  [書籍転載]文法からはじめるプログラミング言語Microsoft Visual C++入門
  Visual C++でマルチスレッド・プログラミング
    1.標準C++におけるマルチスレッド
    2.デッドロック
    3..NETにおけるマルチスレッド
  4.OpenMP

インデックス・ページヘ 「文法からはじめるプログラミング言語Microsoft Visual C++入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間