ページ更新: 2015-09-04 (金) (1201日前)

関連: C++, ソフト/CUDA, ソフト/OpenCV, Windows/VisualStudio, Intel Threding Building Blocks?, Microsoft Parallel Pattern Library?, Microsoft C+AMP?

(2010-10-20)

C/C++/Fortran コンパイラ用の並列処理環境「OpenMP」の情報源とメモ。

目次

[編集]

OpenMP とは #

(2012-09-24)

OpenMP は、C/C++, FORTRAN用の並行処理環境の規格の1つ。 この規格は非営利団体 OpenMP Architecture Review Board によって管理されている。

コンパイラ・ディレクティブ (C/C++の場合 #pragma omp) を用いて、並列化の指示を行う。 OpenMP なし/あり の双方で動作するプログラムを比較的容易に記述することが出来る。

[編集]

OpenMP の特徴 #

(2012-09-24)

  • 多くのコンパイラで標準的に実装されている。
  • SMP 環境 (マルチCPU, マルチコア) を前提とする。
    • Symmetrical Multi Processing: 各CPU/コアが同一メモリを共有する
  • 並列化の指示を与えた部分を OpenMP を無効にすることにより逐次実行することもできる。(ただし、ソースコードがOpenMPに依存しないように書かれた場合のみ)
    • コンパイラディレクティブ(#pragma omp) は、OpenMP が無効なら無視されるので、逐次実行時には影響を与えない。
    • OpenMP が有効なときだけ「_OPENMP」プリプロセッサシンボルが定義されるので、#ifdef / defined() で#pragma以外のOpenMP依存部分を切り出すことが可能
  • 以下の機能を持つ
    • スレッドの作成、起動(fork)、待ち合わせ(join)、pooling。これらはすべて自動的に行われる。
    • ループ内の処理の並列化 (parallel for)
    • 複数のブロックの処理を並列化 (parallel sections)
    • スレッドのスケジューリング
    • スレッド間での変数共有の有無を指定
    • 共有変数の排他処理
    • 還元処理(reduction)の一部で排他処理を自動的に行う
    • 使用するスレッド個数の指定
    • 並列処理のネスト
    • 計時処理
[編集]

OpenMP に向いているコード #

(2012-09-24)

  • 基本的には、計算負荷が大きいループに対して適用すると、比較的簡単に性能向上を果たせる。
    • ループの回数が多く(CPUコア個数の数倍以上)、ループの各繰り返し間に依存性が無い場合
  • (逆に)スレッドの起動(fork)や待ち合わせ(join)を細かく制御することはできない。よって、一般的なスレッドが使われる用途のうち、以下のものには適用できない:
    • スレッドごとに処理内容がまったく異なる場合。例:GUIを持つプログラムが、重い内部処理だけを別スレッドで実行することにより、GUIの応答性が低下するのを避ける
    • スレッドの開始・終了タイミングが大幅に異なる場合や外部要因でスレッドの開始・終了が行われる場合。例:HTTPサーバのように1TCP接続ごとに1スレッドを割り当てる場合や、GUIスレッドとバックグラウンドスレッドのようにスレッドごとに役割が異なる場合。
[編集]

情報源 #

[編集]

ニュース #

[編集]

OpenMP.org #

OpenMP 4.0:

[編集]

cOMPunity - The Community of OpenMP Users #

[編集]

iSUS #

[編集]

Intel (インテルコンパイラー), XLSoft #

(2011-02-02)

インテルコンパイラー向けの資料だが、コンパイラーに依存する内容はほとんど書かれていない。 よって、OpenMP の入門資料として役立つ。

[編集]

Intel Parallel Studio #

OpenMP と Intel TBB に対応した開発統合環境。Windows では Visual Studioに統合して用いる。Intelの製品。評価版あり。

[編集]

Microsoft (Visual Studio, Windows SDK) #

(2011-02-02)

Visual Studio 2005 で OpenMP 2.0 (March 2002) に対応した。プリプロセッサシンボル _OPENMP の値は 200203。

  • なお、2014-08-30 時点の Visual Studio 2013 Update 3 も OpenMP 2.0 である。
[編集]

GCC #

(2014-04-25, 2011-02-02)

gcc 4.2 で OpenMPに正式に対応している。(gcc 4.1で一部対応?) gcc 4.9 で OpenMP 4.0に対応。

コンパイルオプションは「-fopenmp」を用いる。

[編集]

GNU libc++ Parallel Mode #

(2012-09-17, 2012-09-19)

Parallel Mode とは、GNU C++ Library (GNU libstdc++) に含まれる STL の一部 (algorithm) の OpenMP 対応版である。

現時点では <algorithm> と <numeric> の関数の一部に OpenMP 適用版が用意されている。

Paralell Mode の関数を用いるには、次の2つの方法がある。

  1. 暗黙的に使用する:プログラムの修正は不要。コンパイルオプションに「-fopenmp -D_GLIBCXX_PARALLEL」を追加する。
  2. 明示的に使用する:プログラムの修正が必要。#include <parallel/numeric> や #include <paralell/algorithm> を追加し、 std::* の代わりに __gnu_parallel::* の関数を用いる。 さらに、コンパイルオプションに「-fopenmp」 を追加する。

なお、手元でいくつかの環境で試したところ、スレッドが1つの場合は Paralell Mode を用いないほうが、若干速い。 → #ParalellModeMemo

[編集]

LLVM #

(2011-02-02, 2012-09-25)

  • 2012-08-30 LionでOpenMP
    • 動かない/長時間動かすと落ちる、/usr/liggomp.1.dylib が原因、対策は libgomp を自分でビルドする / HPC Mac OS X (ビルド済み)を使う
[編集]

OpenMP Multi-Threaded Template Library #

(2011-02-18)

C++ の STL (Standard Template Library) の algorithm (#include <algorithm>) などを OpenMP 化したライブラリ。 「Common Versatile Multi-purpose Library for C++」の一部でもある。

[編集]

HPC++ Parallel Standard Template Library (PSTL) #

(2012-09-24)

[編集]

Web #

  • OpenMP チュートリアル
    • 全7ページ, 計算機システム(情報科学・知能情報メディア)実験テーマ 「高性能並列プログラミング」 より
  • OpenMP 特集 | iSUS
    • OpenMP* を使用して既存のシリアルコードで並列処理の可能性を見つけよう
    • インテル® IPP における OpenMP* サポートの変更
    • OMP Abort エラーが発生した場合の対処方法
    • OpenMP 特集に向けて
    • OpenMP* 入門
    • インテル® C++/Fortran コンパイラーによる OpenMP* 3.1 仕様のサポート
    • C++ 開発者が陥りやすい OpenMP* の 32 の罠 | iSUS
[編集]

書籍 #

[編集]

ツール #

[編集]

メモ #

[編集]

GNU libc++ Parallel Mode #

(2012-09-17, 2012-09-19)

./#ParallelMode

[編集]

Ubuntu 11.10 64bit (on VMware Workstation), 1,2,4 threads #

(2012-09-17, 2012-09-19)

Ubuntu 11.10 64bit (on VMware Workstation), 2-thread / 4-theead で試してみた。

2-thread のときの性能が 3倍近い (Q9550 で 2.919倍、3930X で 2.789 倍) になる原因はわからない。

VMware で動かしているため、タイマの精度に問題があるのか?、それともメモリアクセスパターンが異なるだろうからキャッシュミスが減ったとか?

計測用プログラム(ソースコード) parallel_libstdcpp.cpp:

  • 補足: 実行所要時間の計測について。
    • MacOS X の場合、#include <mach/mach_time.h> して、mach_absolute_time(), mach_timebase_info() を使うらしい。
    • OpenMP の omp_get_wtime() と omp_get_wtick() を使うと、どのプラットフォームでも動くはず。OpenMP を無効にしたら計測できなくなるけど。
/*
-*- coding: utf-8 mode: cpp -*- 

Linux, Cygwin
$ g++ -Wall -Wextra parallel_libstdcpp.cpp -lrt -fopenmp -o explicit_parallel
$ g++ -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

TDM64-GCC:
$ g++ -Wall -Wextra parallel_libstdcpp.cpp -fopenmp -o explicit_parallel
$ g++ -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

*/

#include <algorithm>
#include <parallel/algorithm>

#include <vector>
#include <iostream>
#include <cstdlib>
#include <ctime>
//#include <random>

#if defined _WIN32 || defined _WIN64 || defined _MSC_VER

#include <windows.h>
#include <psapi.h>

double get_counter_sec() {
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);

    LARGE_INTEGER time;
    QueryPerformanceCounter(&time);

    return static_cast<double>(time.QuadPart) / freq.QuadPart;
}

#elif defined __unix__

#include <sys/types.h>
#include <unistd.h>

double get_counter_sec() {
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return ts.tv_sec + ts.tv_nsec / double(1000*1000*1000);
}

#endif

int main(int, char *[])
{
    srand(0);
    // std::mt19937 engine(0);
    // std::uniform_int<int> distribution(0, 65535);

    const size_t ARRAY_SIZE = 1024 * 1024;
    std::cout << "ARRAY_SIZE = " << ARRAY_SIZE << std::endl;

    std::vector<int> s; s.reserve(ARRAY_SIZE);
    for (size_t i = 0; i < ARRAY_SIZE; ++i) {
        s.push_back( int((rand() >> 2) % 65536) );
        // s.push_back(distribution(engine));
    }

    for (int i = 0; i < 3; ++i) {
        std::vector<int> v(s);

        const double start = get_counter_sec();
        std::sort(v.begin(), v.end());
        const double end   = get_counter_sec();

        std::cout << "std::sort(): elapsed = " << (end - start) << std::endl;
    }

    for (int i = 0; i < 3; ++i) {
        // Explicitly force a call to parallel sort.
        std::vector<int> v(s);

        const double start = get_counter_sec();
        __gnu_parallel::sort(v.begin(), v.end());
        const double end   = get_counter_sec();

        std::cout << "__gnu_parallel::sort(): elapsed = " << (end - start) << std::endl;
    }

    return 0;
}

このプログラムを使って、次の条件を変えて計測する:

  • std::sort (#include <algorithm>) と __gnu_parallel::sort (#include <parallel/algorithm>) の2つを計測する
  • _GLIBCXX_PARALLEL なし / あり
  • OpenMP スレッドの個数 =1, 2, 4

libstdc++ と g++ のバージョン:

$ dpkg -l | grep libstdc++6
ii  libstdc++6           4.6.1-9ubuntu3    GNU Standard C++ Library v3
ii  libstdc++6-4.4-dev   4.4.6-11ubuntu2   GNU Standard C++ Library v3 (development files)
ii  libstdc++6-4.5-dev   4.5.3-9ubuntu1    The GNU Standard C++ Library v3 (development files)
ii  libstdc++6-4.6-dev   4.6.1-9ubuntu3    GNU Standard C++ Library v3 (development files)

$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ビルド:

$ g++ -Wall -Wextra parallel_libstdcpp.cpp -lrt -fopenmp -o explicit_parallel

$ g++ -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

実行 (Core2 Quad Q9550, VMware に4CPUコアを割り当て ARRAY_SIZE= 1024 * 1024)

$ ./implicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.0879943
std::sort(): elapsed = 0.0848567
std::sort(): elapsed = 0.0844018
__gnu_parallel::sort(): elapsed = 0.0874939
__gnu_parallel::sort(): elapsed = 0.0869579
__gnu_parallel::sort(): elapsed = 0.0856276

$ ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.419451
std::sort(): elapsed = 0.417351
std::sort(): elapsed = 0.420393
__gnu_parallel::sort(): elapsed = 0.0866299
__gnu_parallel::sort(): elapsed = 0.08391
__gnu_parallel::sort(): elapsed = 0.0792465

$ OMP_NUM_THREADS=2 ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.421517
std::sort(): elapsed = 0.419009
std::sort(): elapsed = 0.425709
__gnu_parallel::sort(): elapsed = 0.155574
__gnu_parallel::sort(): elapsed = 0.152963
__gnu_parallel::sort(): elapsed = 0.151587

$ OMP_NUM_THREADS=1 ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.422655
std::sort(): elapsed = 0.425394
std::sort(): elapsed = 0.424165
__gnu_parallel::sort(): elapsed = 0.471778
__gnu_parallel::sort(): elapsed = 0.475645
__gnu_parallel::sort(): elapsed = 0.474769

実行 (Core i7 3980X, VMware に2CPUコアを割り当て, ARRAY_SIZE = 1024 * 1024)

$ ./implicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.106128
std::sort(): elapsed = 0.105796
std::sort(): elapsed = 0.104501
__gnu_parallel::sort(): elapsed = 0.104554
__gnu_parallel::sort(): elapsed = 0.104465
__gnu_parallel::sort(): elapsed = 0.104423

$ ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.286969
std::sort(): elapsed = 0.28658
std::sort(): elapsed = 0.286289
__gnu_parallel::sort(): elapsed = 0.105846
__gnu_parallel::sort(): elapsed = 0.105538
__gnu_parallel::sort(): elapsed = 0.104874

$ OMP_NUM_THREADS=1 ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.288237
std::sort(): elapsed = 0.288404
std::sort(): elapsed = 0.287212
__gnu_parallel::sort(): elapsed = 0.331541
__gnu_parallel::sort(): elapsed = 0.332141
__gnu_parallel::sort(): elapsed = 0.331636

(2012-09-19)

std::sort だけ(std_sort.cpp)、__gnu_parallel::sortだけ (parallel_sort.cpp) のコードを用意してビルドし、Cachegrind してみた。 refs も misses も桁は同じだが……。

ちなみに、KCachegrind で調べると、 __gnu_cxx::__normal_iterator<> と __memmove_ssse3_back (たぶん std::vector<int> v(s); で使ってる) が、refs も misses も多い。これ以上はライブラリソースコード見たほうがよいだろうから、当面は追求しない。

$ valgrind --tool=cachegrind ./std_sort
==2274== Cachegrind, a cache and branch-prediction profiler
==2274== Copyright (C) 2002-2010, and GNU GPL'd, by Nicholas Nethercote et al.
==2274== Using Valgrind-3.6.1-Debian and LibVEX; rerun with -h for copyright info
==2274== Command: ./std_sort
==2274==
ARRAY_SIZE = 1048576
std::sort(): elapsed = 14.553
std::sort(): elapsed = 14.5423
std::sort(): elapsed = 14.5295
==2274==
==2274== I   refs:      4,324,793,723
==2274== I1  misses:            2,329
==2274== LLi misses:            2,315
==2274== I1  miss rate:          0.00%
==2274== LLi miss rate:          0.00%
==2274==
==2274== D   refs:      2,798,578,362  (1,678,353,195 rd   + 1,120,225,167 wr)
==2274== D1  misses:        2,663,281  (    2,394,202 rd   +       269,079 wr)
==2274== LLd misses:          458,693  (      230,066 rd   +       228,627 wr)
==2274== D1  miss rate:           0.0% (          0.1%     +           0.0%  )
==2274== LLd miss rate:           0.0% (          0.0%     +           0.0%  )
==2274==
==2274== LL refs:           2,665,610  (    2,396,531 rd   +       269,079 wr)
==2274== LL misses:           461,008  (      232,381 rd   +       228,627 wr)
==2274== LL miss rate:            0.0% (          0.0%     +           0.0%  )

$ valgrind --tool=cachegrind ./parallel_sort
==2260== Cachegrind, a cache and branch-prediction profiler
==2260== Copyright (C) 2002-2010, and GNU GPL'd, by Nicholas Nethercote et al.
==2260== Using Valgrind-3.6.1-Debian and LibVEX; rerun with -h for copyright info
==2260== Command: ./parallel_sort
==2260==
ARRAY_SIZE = 1048576
__gnu_parallel::sort(): elapsed = 10.1422
__gnu_parallel::sort(): elapsed = 9.92051
__gnu_parallel::sort(): elapsed = 9.91161
==2260==
==2260== I   refs:      2,902,486,501
==2260== I1  misses:            5,332
==2260== LLi misses:            5,027
==2260== I1  miss rate:          0.00%
==2260== LLi miss rate:          0.00%
==2260==
==2260== D   refs:      1,908,302,435  (1,196,395,228 rd   + 711,907,207 wr)
==2260== D1  misses:        3,016,637  (    2,346,325 rd   +     670,312 wr)
==2260== LLd misses:        1,039,349  (      431,922 rd   +     607,427 wr)
==2260== D1  miss rate:           0.1% (          0.1%     +         0.0%  )
==2260== LLd miss rate:           0.0% (          0.0%     +         0.0%  )
==2260==
==2260== LL refs:           3,021,969  (    2,351,657 rd   +     670,312 wr)
==2260== LL misses:         1,044,376  (      436,949 rd   +     607,427 wr)
==2260== LL miss rate:            0.0% (          0.0%     +         0.0%  )
[編集]

Cygwin 1.7.16-1 (ligstdc++ 4.5.3-3) #

(2012-09-19)

計測に使ったプログラムは ./#Ubuntu1110_64_VM と同じ。

  • Windows XP 上で計測した。計測結果がUbuntuよりばらつくし(精度が低い)、分解能も低い。
$ g++ --version
g++ (GCC) 4.5.3
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cygcheck -cd | grep stdc
libstdc++6           4.5.3-3
libstdc++6-devel     4.5.3-3

$ cygcheck -cd cygwin
Cygwin Package Information
Package              Version
cygwin               1.7.16-1

$ g++ -Wall -Wextra parallel_libstdcpp.cpp -lrt -fopenmp -o explicit_parallel
$ g++ -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

$ cat /proc/cpuinfo | grep model | sort -u
model name      : Intel(R) Core(TM)2 Duo CPU     E8500  @ 3.16GHz
model           : 23

$ ./implicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.109375
std::sort(): elapsed = 0.125
std::sort(): elapsed = 0.140625
__gnu_parallel::sort(): elapsed = 0.125
__gnu_parallel::sort(): elapsed = 0.125
__gnu_parallel::sort(): elapsed = 0.140625

$ ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.34375
std::sort(): elapsed = 0.34375
std::sort(): elapsed = 0.34375
__gnu_parallel::sort(): elapsed = 0.125
__gnu_parallel::sort(): elapsed = 0.125
__gnu_parallel::sort(): elapsed = 0.140625

$ OMP_NUM_THREADS=1 ./explicit_parallel
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.34375
std::sort(): elapsed = 0.34375
std::sort(): elapsed = 0.34375
__gnu_parallel::sort(): elapsed = 0.390625
__gnu_parallel::sort(): elapsed = 0.40625
__gnu_parallel::sort(): elapsed = 0.390625
[編集]

TDM-GCC (tdm64-gcc-4.6.1, tdm64-gcc-4.7.1-2) #

(2012-09-21)

計測に使ったプログラムは ./#Ubuntu1110_64_VM と同じ。

TDM64-GCC-4.6.1:

C:> C:\TDM64-GCC-4.6.1\mingwvars.bat

Setting up environment for using MinGW with GCC from C:\TDM64-GCC-4.6.1\.

C:> g++ --version
g++ (tdm64-1) 4.6.1
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

C:> g++ -m32 -Wall -Wextra parallel_libstdcpp.cpp -fopenmp -o explicit_parallel

C:> g++ -m32 -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

C:> implicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.120652
std::sort(): elapsed = 0.12115
std::sort(): elapsed = 0.10869
__gnu_parallel::sort(): elapsed = 0.11732
__gnu_parallel::sort(): elapsed = 0.108439
__gnu_parallel::sort(): elapsed = 0.109112

C:> explicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.320039
std::sort(): elapsed = 0.318552
std::sort(): elapsed = 0.319395
__gnu_parallel::sort(): elapsed = 0.119199
__gnu_parallel::sort(): elapsed = 0.1221
__gnu_parallel::sort(): elapsed = 0.109956

C:> set OMP_NUM_THREADS=1
C:> explicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.318986
std::sort(): elapsed = 0.319036
std::sort(): elapsed = 0.31948
__gnu_parallel::sort(): elapsed = 0.365821
__gnu_parallel::sort(): elapsed = 0.366088
__gnu_parallel::sort(): elapsed = 0.367129

TDM64-GCC-4.7.1-2:

C:> C:\TDM64-GCC-4.7.1\mingwvars.bat

Setting up environment for using MinGW with GCC from c:\TDM64-GCC-4.7.1\.

C:> g++ --version
g++ (tdm64-1) 4.7.1
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

C:> g++ -m32 -Wall -Wextra parallel_libstdcpp.cpp -fopenmp -o explicit_parallel

C:> g++ -m32 -Wall -Wextra -D_GLIBCXX_PARALLEL parallel_libstdcpp.cpp -fopenmp -o implicit_parallel

C:> implicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.123822
std::sort(): elapsed = 0.124058
std::sort(): elapsed = 0.110214
__gnu_parallel::sort(): elapsed = 0.119833
__gnu_parallel::sort(): elapsed = 0.110097
__gnu_parallel::sort(): elapsed = 0.12179

C:> explicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.311162
std::sort(): elapsed = 0.310624
std::sort(): elapsed = 0.310892
__gnu_parallel::sort(): elapsed = 0.121613
__gnu_parallel::sort(): elapsed = 0.124109
__gnu_parallel::sort(): elapsed = 0.110146

C:> set OMP_NUM_THREADS=1
C:> explicit_parallel.exe
ARRAY_SIZE = 1048576
std::sort(): elapsed = 0.311342
std::sort(): elapsed = 0.310794
std::sort(): elapsed = 0.31138
__gnu_parallel::sort(): elapsed = 0.361351
__gnu_parallel::sort(): elapsed = 0.360332
__gnu_parallel::sort(): elapsed = 0.362469
[編集]

Microsoft Visual Studio (2005,2008,2010,2012) #

(2011-01-09)

(1) OpenMP は、Visual Studio 2005,2008,2010 の Professional Edition と、VisualStudio 2012 (Visaul Studio 2012 Express for Desktop を含む) でサポートされている。 これらの環境では、 Visual Studio がインストールされていれば、OpenMPを用いるプログラムをビルドできる。

OpenMP を使うには、「プロジェクトのプロパティ→構成プロパティ→C/C++→言語→OpenMPのサポート (/openmp)」を有効にする。

(2) Visual Studio 2005, 2008 の Express Edition, Standard Edition では Windows SDK に含まれるヘッダファイルとライブラリ (Release版のみ。Debug版は含まれていない) を用いれば使える。

参考:

(3) Visual Studio 2010 の Express Edition では、対応する Windows SDK に OpenMP のファイルが含まれていないため、使えない。

(4) Visual Studio 2012 では、すべてのエディションで OpenMP が使える。

[編集]

Visual Studio 2005 Standard Edition #

  • Visual Studio 2005 Standard SP1 と Windows SDK 6.1 (6.1.6000.16384.10.WindowsSDK_Vista_Feb2007Update_rtm.DVD.Rel.iso) の「Documentation, Samples 以外」をインストールし、「Integrate Windows SDK with Visual Studio 2005」したが、OpenMP は使えなかった。
  • さらに、Visual Studio 2005 にライブラリとヘッダのディレクトリを追加したら、(Relelae では) OpenMP が使えるようになった。Debug では未確認。
    • 「ツール→オプション→プロジェクトおよびソリューション→VC++ ディレクトリ」にて、 「ライブラリ ファイル」の2番目に「C:\Program Files\Microsoft SDKs\Windows\v6.0\VC\LIB」を追加、 「インクルード ファイル」の3番目に「C:\Program Files\Microsoft SDKs\Windows\v6.0\VC\INCLUDE」を追加
[編集]

Visual Studio 2008 Express Edition, Standard Edition #

  • Visual Studio 2008 Standard SP1 と Windows SDK for Windows 7 and .NET Framework 3.5 SP1 (ISO) (Windows SDK 7.0) の「Documentation, Samples 以外」をインストールし、 「Windows SDK Configuration Tool」を実行すると、(Release では) OpenMP が 使えるようになった。Debug では未確認。(Windows 7 64bit 上で、AMD64用のWindows SDKを用いた)。
  • Visual Studio 2008 Express SP1 も同様の手順でOK
[編集]

Visual Studio 2010 Express Edition #

OpenMP は使えない。→ ./#memo_VisualStudio

[編集]

Visual Studio Express 2012 for Windows Desktop #

(2012-09-18, 2012-09-20)

Visual Studio 2012 では、すべてのエディションで OpenMP が使えるようになった:

確認事項:

  • Release/Debug の双方で、32bit/64bit のビルドと実行を確認する → すべてOK

調査用ソースコード (OpenMP-check.cpp):

#include <iostream>
#include <vector>

#include <omp.h>

int main() {
    using std::cout;
    using std::endl;

#if defined _WIN64 // MEMO: _WIN64 とともに _WIN32 も定義されるので、先に _WIN64 を検査すること。
    cout << "64bit (defined _WIN64)" << endl;
#elif defined _WIN32
    cout << "32bit (defined _WIN32)" << endl;
#endif

    cout << "_MSC_VER = " << _MSC_VER << endl
         << "_OPENMP= " << _OPENMP << endl
         << "omp_get_num_procs()= " << omp_get_num_procs() << endl
         << "omp_get_max_threads()= " << omp_get_max_threads() << endl
		 ;

    // 配列への代入を並列実行する。代入する値は「スレッド番号」
    const int n_num = 32;
    std::vector<int> num(n_num);

    // このループを並列実行する
    #pragma omp parallel for
    for (int i = 0; i < n_num; ++i) {
        num[i] = omp_get_thread_num();
    }

    // 代入された値(スレッド番号)を表示する
    for (size_t i = 0, s = num.size(); i < s; ++i) {
        cout << "num[" << i << "]= " << num[i] << endl;
    }

    return 0;
}

ビルドのオプションは、次の一箇所だけを変更した:

  • 構成プロパティ -> C/C++ -> 言語 -> OpenMPのサポート = はい(/openmp)

ビルドした実行ファイルがちゃんと 32bit/64bit 版かどうかを確認した。OK:

C:> "%VS110COMNTOOLS%\vsvars32.bat"

C:> dumpbin /headers Release\OpenMP-check.exe | findstr "machine"
             14C machine (x86)
                   32 bit word machine

C:> dumpbin /headers Debug\OpenMP-check.exe | findstr "machine"
             14C machine (x86)
                   32 bit word machine

C:> dumpbin /headers x64\Release\OpenMP-check.exe | findstr "machine"
            8664 machine (x64)

C:> dumpbin /headers x64\Debug\OpenMP-check.exe | findstr "machine"
            8664 machine (x64)

実行結果 (CPU = Core2 Quad Q6600):

C:> Release\OpenMP-check.exe
32bit (defined _WIN32)
_MSC_VER = 1700
_OPENMP= 200203
omp_get_num_procs()= 4
omp_get_max_threads()= 4
... (以下略)

C:> Debug\OpenMP-check.exe
32bit (defined _WIN32)
_MSC_VER = 1700
_OPENMP= 200203
omp_get_num_procs()= 4
omp_get_max_threads()= 4
... (以下略)

C:> x64\Release\OpenMP-check.exe
64bit (defined _WIN64)
_MSC_VER = 1700
_OPENMP= 200203
omp_get_num_procs()= 4
omp_get_max_threads()= 4
... (以下略)

C:> x64\Debug\OpenMP-check.exe
64bit (defined _WIN64)
_MSC_VER = 1700
_OPENMP= 200203
omp_get_num_procs()= 4
omp_get_max_threads()= 4
... (以下略)
[編集]

Windows SDK と Visual Studio のOpenMP ヘッダファイル/ライブラリの位置と大きさ #

(2011-01-13)

Windows SDK 6.0, Windows SDK 7.0, Visual Studio 2010 Professional をインストールしたPCにて、omp.h, ompassem.h, vcomp.lib, vcompd.lib の位置などを調べた。

  • なお、Windows SDK 7.1 (リリース日 2010-05-19) には これらのヘッダファイルやライブラリファイルは見あたらなかった
  • vcomp*.dll, vcompd*.dll は調べてない。
    • VC2005/Windows SDK 6.0 = vcomp.dll
    • VC2008/Windows SDK 7.0 = vcomp90.dll, vcomp90d.dll, vcomp90ui.dll
    • VS2010 = vcomp100.dll, vcomp100d.dll, vcomp100ui.dll

omp.h (dir /s "C:\Program Files\omp.h"):

 C:\Program Files\Microsoft SDKs\Windows\v6.0\VC\INCLUDE\omp.h          2006/12/02 00:46  5,310
 C:\Program Files\Microsoft Visual Studio 9.0\VC\include\omp.h          2006/06/21 13:13  5,316
 C:\Program Files\Microsoft Visual Studio 10.0\VC\include\omp.h         2009/08/31 02:35  2,431

ompassem.h:

 C:\Program Files\Microsoft Visual Studio 9.0\VC\include\ompassem.h     2008/07/29 04:13    948 

vcomp.lib (dir /s "C:\Program Files\vcomp.lib"):

 C:\Program Files\Microsoft SDKs\Windows\v6.0\VC\LIB\vcomp.lib          2006/12/02 00:46  26,812
 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\vcomp.lib          2008/07/29 04:44  28,446
 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\amd64\vcomp.lib    2008/07/29 04:16  27,890
 C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\vcomp.lib         2010/03/18 01:56  28,580
 C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\amd64\vcomp.lib   2010/03/18 06:28  28,004

vcompd.lib (dir /s "C:\Program Files\vcompd.lib"):

 C:\Program Files\Microsoft SDKs\Windows\v6.0\VC\LIB\vcompd.lib         2006/12/02 00:46  26,946
 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\vcompd.lib         2008/07/29 04:44  28,574
 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\amd64\vcompd.lib   2008/07/29 04:16  27,998
 C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\vcompd.lib        2010/03/18 01:56  28,686
 C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\amd64\vcompd.lib  2010/03/18 06:28  28,130
C:> dir "c:\program files\common files\Merge Modules\*OpenMP*"
...
 c:\program files\common files\Merge Modules のディレクトリ

2010/03/19  04:44            60,416 Microsoft_VC100_DebugOpenMP_x64.msm
2010/03/19  07:24            60,416 Microsoft_VC100_DebugOpenMP_x86.msm
2010/03/19  03:06            51,712 Microsoft_VC100_OpenMP_x64.msm
2010/03/19  05:49            50,176 Microsoft_VC100_OpenMP_x86.msm
2009/07/12  01:58           136,704 Microsoft_VC80_DebugOpenMP_x86.msm
2009/07/12  01:58           133,632 Microsoft_VC80_DebugOpenMP_x86_x64.msm
2009/07/12  01:59           124,928 Microsoft_VC80_OpenMP_x86.msm
2009/07/12  01:59           131,584 Microsoft_VC80_OpenMP_x86_x64.msm
2007/11/07  03:38           112,128 Microsoft_VC90_DebugOpenMP_x86.msm
2007/11/07  07:21           112,640 Microsoft_VC90_DebugOpenMP_x86_x64.msm
2007/11/07  03:42           102,400 Microsoft_VC90_OpenMP_x86.msm
2007/11/07  07:26           103,424 Microsoft_VC90_OpenMP_x86_x64.msm
2009/07/12  01:58            90,624 policy_8_0_Microsoft_VC80_DebugOpenMP_x86.msm
2009/07/12  01:58            90,624 policy_8_0_Microsoft_VC80_DebugOpenMP_x86_x64.msm
2009/07/12  01:59            89,600 policy_8_0_Microsoft_VC80_OpenMP_x86.msm
2009/07/12  01:59            89,600 policy_8_0_Microsoft_VC80_OpenMP_x86_x64.msm
2007/11/07  04:30            75,776 policy_9_0_Microsoft_VC90_DebugOpenMP_x86.msm
2007/11/07  08:16            74,752 policy_9_0_Microsoft_VC90_DebugOpenMP_x86_x64.msm
2007/11/07  04:35            74,752 policy_9_0_Microsoft_VC90_OpenMP_x86.msm
2007/11/07  08:22            75,264 policy_9_0_Microsoft_VC90_OpenMP_x86_x64.msm
              20 個のファイル           1,841,152 バイト
[編集]

サンプルコード(1) #

C++ code - 44 lines - codepad

OpenMP の以下の関数とpragmaを用いている

  • omp_get_thread_num()
  • omp_get_num_threads()
  • #pragma omp parallel reduction(+:var)
  • #pragma omp flush(var)
  • Visual C++ 2005 Standard + Widnows SDK 6.0, Visual Studio 2008 Express/Standard SP1 + Windows SDK 7.0, Visual Studio 2010 Professional で動作確認
    • メモ: omp.h 内でライブラリを指定しているので(#pragma comment(lib, "vcomp"))、常に#include <omp.h> が必要 → #ifdef _OPENMP 〜 #endif で囲っておけばよい。
  • Ubuntu 10.04.1 LTS の g++ 4.4.3-1ubuntu1「g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3」で動作確認 (オプション -fopenmp)
  • Cygwin 1.7.7-1, gcc4-g++ 4.3.4-3「g++ (GCC) 4.3.4 20090804 (release) 1」で動作確認 (オプション -fopenmp)
[編集]

サンプルコード(2) 「parallel for」 #

(2011-02-03)

参考:「インテル C/C++ コンパイラー OpenMP 活用ガイド」 pi_omp.c

OpenMP なし:

#include <cstdio>

int main() {
    const int num_steps = 2 * 10000 * 10000;
    const double step = 1.0 / (double) num_steps;

    double sum = 0.0;
    for (int i = 0; i <= num_steps; i++) {
        const double x = (i - 0.5) * step;
        sum += 4.0 / (1.0 + x * x);
    }

    const double pi = step * sum;
    std::printf("PI=%lf\n", pi);

    return 0;
}

OpenMP化したソースコード

  • OpenMPが無くても動作するようにした
  • Visual Studio/C++ のために omp.hを常にincludeするようにした
#include <cstdio>

#ifdef _OPENMP
#include <omp.h>
#endif

int main() {
    const int num_steps = 2 * 10000 * 10000;
    const double step = 1.0 / (double) num_steps;

    double sum = 0.0;

    #pragma omp parallel for reduction(+:sum)
    for (int i = 0; i <= num_steps; i++) {
        const double x = (i - 0.5) * step;
        sum += 4.0 / (1.0 + x * x);
    }

    const double pi = step * sum;
    std::printf("PI=%lf\n", pi);

    return 0;
}

コンパイルと実行 (Cygwin 1.7.7-1, g++ 4.3.4, Core2Duo (2C2T))

★ OpenMP 無効
$ g++ -Wall -Wextra pi_omp.cpp -o pi && time ./pi
pi_omp.cpp:13: 警告: ignoring #pragma omp parallel
PI=3.141593

real    0m1.459s  ★
user    0m1.483s
sys     0m0.015s

★ OpenMP 有効
$ g++ -Wall -Wextra -fopenmp pi_omp.cpp -o pi_omp && time ./pi_omp
PI=3.141593

real    0m0.755s  ★
user    0m1.374s
sys     0m0.015s

最大スレッド数を変更して実行 (環境変数 OMP_NUM_THREADS):

$ export OMP_NUM_THREADS=2
$ time ./pi_omp
PI=3.141593

real    0m0.717s  ★
user    0m1.358s
sys     0m0.000s


$ export OMP_NUM_THREADS=1
$ time ./pi_omp
PI=3.141593

real    0m1.413s  ★
user    0m1.421s
sys     0m0.015s
[編集]

サンプルコード(3) 「parallel sections」 #

(2011-02-04)

参考:「インテル C/C++ コンパイラー OpenMP 活用ガイド」 pi_omp.c

pi_section.cpp (負荷の大きい処理 (calc_pi()) を2つ実行する):

#include <cstdio>

double calc_pi(const int num_steps) {
    const double step = 1.0 / (double) num_steps;

    double sum = 0.0;
    for (int i = 0; i <= num_steps; i++) {
        const double x = (i - 0.5) * step;
        sum += 4.0 / (1.0 + x * x);
    }

    const double pi = step * sum;
    return pi;
}

int main() {
    const double pi_1 = calc_pi(1 * 10000 * 10000);
    const double pi_2 = calc_pi(2 * 10000 * 10000);

    std::printf("PI_1=%lf\n", pi_1);
    std::printf("PI_2=%lf\n", pi_2);

    return 0;
}

pi_section をビルドして、実行する:

$ g++ -Wall -Wextra pi_section.cpp -o pi_section && time ./pi_section
PI_1=3.141593
PI_2=3.141593

real    0m2.113s  ★
user    0m2.139s
sys     0m0.000s

pi_omp_section.cpp (OpenMPでcalc_pi()を2つ同時に実行するように修正):

#include <cstdio>

#ifdef _OPENMP
#include <omp.h>
#endif

double calc_pi(const int num_steps) {
    const double step = 1.0 / (double) num_steps;

    double sum = 0.0;
    for (int i = 0; i <= num_steps; i++) {
        const double x = (i - 0.5) * step;
        sum += 4.0 / (1.0 + x * x);
    }

    const double pi = step * sum;
    return pi;
}

int main() {
    double pi_1;
    double pi_2;

    #pragma omp parallel sections
    {
        #pragma omp section
        pi_1 = calc_pi(1 * 10000 * 10000);

        #pragma omp section
        pi_2 = calc_pi(2 * 10000 * 10000);
    }

    std::printf("PI_1=%lf\n", pi_1);
    std::printf("PI_2=%lf\n", pi_2);

    return 0;
}

pi_omp_section をビルド、実行する:

$ g++ -Wall -Wextra -fopenmp pi_omp_section.cpp -o pi_omp_section && time ./pi_omp_section
PI_1=3.141593
PI_2=3.141593

real    0m1.418s  ★
user    0m2.077s
sys     0m0.015s