teratail header banner
teratail header banner
質問するログイン新規登録
Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

並列処理

複数の計算が同時に実行される手法

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

2回答

886閲覧

C++でOpenMPを使って並列化したい。

hanamur

総合スコア45

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

並列処理

複数の計算が同時に実行される手法

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2022/05/11 21:05

0

0

Visual Studio2019でC++を使ってプログラムを書いています。

mp.cpp

1#include <iostream> 2#include <stdio.h> 3#include <stdlib.h> 4#include<time.h> 5 6int main() { 7 int i, j; 8 double k = 0; 9 clock_t start = clock(); 10#pragma omp parallel for 11 for (i = 0; i <1000000 ; i++) { 12 for (j = 0; j < 100; j++) { 13#pragma omp atomic 14 k += sqrt(i)-j; 15 } 16 } 17 clock_t end = clock(); 18 19 const double time = static_cast<double>(end - start) / CLOCKS_PER_SEC * 1000.0; 20 printf("time %lf[ms]\n", time); 21 printf(" k = %lf,\n", k); 22}

notmp.cpp

1#include <iostream> 2#include <stdio.h> 3#include <stdlib.h> 4#include<time.h> 5 6int main() { 7 int i, j; 8 double k = 0; 9 clock_t start = clock(); 10 for (i = 0; i <1000000 ; i++) { 11 for (j = 0; j < 100; j++) { 12 k += sqrt(i)-j; 13 } 14 } 15 clock_t end = clock(); 16 17 const double time = static_cast<double>(end - start) / CLOCKS_PER_SEC * 1000.0; 18 printf("time %lf[ms]\n", time); 19 printf(" k = %lf,\n", k); 20}

mp.cppが並列化したプログラムで、notmp.cppが同じ内容で非並列化のプログラムです。
実際に実行してみた出力結果は、

mp.cppの場合

1time 17765.000000[ms] 2k = 61624080556.706154

notmp.cppの場合

1time 1515.000000[ms] 2k = 61716616645.940987

となって、並列化しない場合のほうが10倍近く高速であるという結果になりました。
また、計算結果についても、notmp.cppでは何度やっても全く同じ結果が出力されるのに対して、
mp.cppではプログラムを動かすたびに違う結果が出てきます。
(誤差ではある。例えば61656649114.643929)
以上、余計に時間がかかる。計算結果が正確ではない。の二つの問題があり、この原因と解決策を知りたくて質問させていただきました。どうかよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tmp

2022/05/12 00:10

結果のkがかなり大きくなっているので最後の方が情報落ちが多くなりますよね。mp.cppでは、足す順番がかわるので、その誤差も変わると考えられます。
guest

回答2

0

OpenMPもしばらく触ってない上に、コードも動かしてないのであくまで想像の上での回答で申し訳ないです。

CのOpenMPのpararell指示句で暗黙的にprivate変数になるのは並列化されたループの制御変数だけだったと思います(fortranはもうちょっと気が利いた気がしますけど)。
そのため内側のループのjはprivate変数となっておらず、各スレッドが共有する変数となってしまい、実行するたびに結果が異なってしまう原因になっていると考えられます。
またatomic指示句には若干のオーバヘッドが伴います。そのため並列数やループ数にもよりますが、オーバヘッドが無視できない状態になっているのではないかと考えられます。atomicやcriticalを安易に使用すると性能が出ない、なんていうことはよくあることなので…。こういった計算結果を足し合わせて最終的に1つの計算結果を出したい、という場合にはreduction指示句を使用する場合が多いのではないかと思います。以下のページが参考になるのではないでしょうか。

もくもくブログ OpenMPによるfor文の並列化

投稿2022/05/11 23:45

Munosuke222

総合スコア163

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

余計に時間がかかる。計算結果が正確ではない。

Munosuke222さんの回答内容そのままですが、下記のように変更したら、notmp.cppよりも速く、実行する度に計算結果が変わらなくなりました
ただし、notmp.cppの計算結果と同じではありませんでした (違いは1.0未満ですが)

c++

1#include <iostream> 2#include <stdio.h> 3#include <stdlib.h> 4#include<time.h> 5 6int main() { 7 int i, j; 8 double k = 0; 9 clock_t start = clock(); 10//#pragma omp parallel for 11//#pragma omp parallel for private(i, j) 12#pragma omp parallel for private(i, j) reduction(+:k) 13 for (i = 0; i <1000000 ; i++) { 14 for (j = 0; j < 100; j++) { 15//#pragma omp atomic 16 k += sqrt(i)-j; 17 } 18 } 19 clock_t end = clock(); 20 21 const double time = static_cast<double>(end - start) / CLOCKS_PER_SEC * 1000.0; 22 printf("time %lf[ms]\n", time); 23 printf(" k = %lf,\n", k); 24}

 
Visual Studio 2017で、下記のコマンドでコンパイルしました

cmd

1cl /O2 /arch:AVX /fp:fast /EHsc /MT /openmp /wd 4819 mp.cpp

投稿2022/05/12 05:42

jbpb0

総合スコア7658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbpb0

2022/05/12 06:20 編集

当方のPCでの結果は、下記の通り [mp.cppの場合] time 13.000000[ms] k = 61716616645.882156, [notmp.cppの場合] time 55.000000[ms] k = 61716616645.764549, コンパイルコマンドに「/fp:fast」を付けなかった場合の結果は下記の通りで、notmp.cppの結果の「k」は、質問者さんの結果と同じでした [mp.cppの場合] time 17.000000[ms] k = 61716616645.887421, [notmp.cppの場合] time 118.000000[ms] k = 61716616645.940987, 【追記】 コンパイルコマンドに「/O2 /arch:AVX /fp:fast」を付けなかった場合の結果は下記の通り [mp.cppの場合] time 150.000000[ms] k = 61716616645.887421, [notmp.cppの場合] time 1124.000000[ms] k = 61716616645.940987,
Munosuke222

2022/05/12 15:36

これくらいの計算だと並列化とベクトル化で随分と速くなるんですねえ…
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問