VisualC++で並列プログラミング(OpenMP)を試してみた

CodeZine - 基礎から学ぶOpenMP 第1回 - OpenMPの基礎構文

OpenMPというのは共有メモリ並列プログラミングの標準 API 。

VisualStudioでは2005から使えるらしい。

まずはHello World

環境はVC++2008。
新規プロジェクトでWIN32アプリケーションを作って作業開始だ。

コードはコピペしてこう書いた。

#include "stdafx.h"
#include <omp.h>

int _tmain(int argc, _TCHAR* argv[])
{
    #pragma omp parallel
    printf("Hello, OpenMP!\n");
    return 0;
}

しかし、これだけじゃ並列プログラムにならない。


メニューのプロジェクトから一番下にあるプロジェクトのプロパティを開いて
[プロジェクトのプロパティ]-[構成プロパティ]-[C++]-[言語]のプロパティページで、「OpenMPのサポート」を「はい」にする。


さらになぜだかソリューションのプロパティで、このサンプルプログラムのプロジェクトを構成をReleaseに変更してからビルド。なにやら必要なファイルを作る作業らしい。


続いてデバッグに戻して実行してみると、あれれこんなダイアログが。


なにやらvcomp90d.dllが足りないらしい。
このファイルはVisual Studio 2008のStandardバージョン以上ならば「C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugOpenMP」にあるので、実行ファイルと同じ場所にコピーする。
面倒だなぁ・・・


これで実行する。
おお、コア数分Hello,Worldが出てるよ。

このHello Worldから分ること

OpneMPを使うには

  • omp.hをインクルードすること
  • プリプロセッサディレクティブのプラグマ#pragma omp parallelにより、コンパイラに並列処理を指示
  • vcomp90d.dllコピー必要


ちなみにOpenMPを使ったソフトを他環境で動かすには再配布パッケージが必要です。

#pragma omp parallelと並列化されるコードの関係

次の行、スコープが並列化される。
こんな感じ。

int _tmain(int argc, _TCHAR* argv[])
{
    #pragma omp parallel
    printf("このコードは並列化されます。\n");
    printf("このコードはどうなる?\n");
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    #pragma omp parallel 
    {
        printf("このコードは並列化されます。\n");
        printf("このコードはどうなる?\n");
    }
    return 0;
}

スレッド数をコントロールしよう

上のコードではCPUのコア数分スレッドができてたけど、コントロールできます。
APIを使います。

  • omp_get_dynamic()
    • スレッド数の動的調整がON/OFFを取得
  • omp_set_dynamic()
    • スレッド数の動的調整のON/OFF設定
    • 動的調節がON時は、CPUのコア数に応じて処理が並列化
  • omp_get_num_threads()
    • スレッド数を取得
    • parallel構文の範囲以外では必ず1を返すので注意
  • omp_set_num_threads()
    • スレッド数を指定
    • もし指定した数のスレッド数が生成できなければどうなるかは未定義


サンプルコード

#include "stdafx.h"
#include <omp.h>

int _tmain(int argc, _TCHAR* argv[])
{
    int threadCount = 4; /*スレッド数*/

    //初期状態を表示
    printf( "初期状態の動的調節機能は%s\n\n", omp_get_dynamic() == 0 ?  "無効" : "有効" );

    /*スレッドの動的調節を有効にする*/
    omp_set_dynamic( 1 );
    printf ( "動的調節機能を有効化しました\n" );
    omp_set_num_threads( threadCount );
    #pragma omp parallel
    {
        printf ( "現在のスレッド数は%dです。\n", omp_get_num_threads());
    }
    printf( "現在動的調節機能は%s\n\n", omp_get_dynamic() == 0 ?  "無効" : "有効" );

    /*スレッド数を指定して並列実行*/
    omp_set_dynamic( 0 );
    omp_set_num_threads( threadCount );
    printf ( "動的調節機能を無効化しました。指定したスレッド数は%dです。\n", threadCount );
    #pragma omp parallel
    {
        printf ( "現在のスレッド数は%dです。\n", omp_get_num_threads());
    }
    printf( "現在動的調節機能は%s\n\n", omp_get_dynamic() == 0 ?  "無効" : "有効" );

    //終了
    printf( "\n\n" );
    return 0;
}

結果

複数のスレッドで実行したくない処理が含まれている場合の書き方

#pragma omp singleという構文を追加。

// testopenmp_20100813.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include <omp.h>

int _tmain(int argc, _TCHAR* argv[])
{
    #pragma omp parallel
    {
        printf ( "複数回表示\n" );
        #pragma omp single
        {
            printf("1回だけ表示\n");
        }
        printf ( "複数回表示\n" );
    }

    //終了
    printf( "\n\n" );
    return 0;
}

結果

感想

うん、環境さえできれば、案外簡単にかけるんだなー