ようこそNICのホームページへ(なが〜い試運転中)   
HP パソコン 健康 落語 海外 思うまま 電子工作

cpuを有効に使う、fortranでの並列処理の時間比較、openmpを使う

Last update:2018/03/07    Build:2018/03/02



素数の積が回文数となる へ  cpuを有効に使う、fortranでの並列処理の時間比較、openmpを使う へ 

 
表−1 時間比較へ  表−2 schedule比較へ  表−3 CPUtimeと実時間比較へ


素数の積が回文数となるパターンを調べている時、vbaでは時間が掛かり、 調べる範囲を拡げるのに限界がきた。
そこで高速化を図るため
1. コンパイラー言語 → 昔使ったFORTRANが使えないか?
2. 4コアcpuを有効に活用して、並列処理ができないか
調べてみることにした。

openMPというのがあり、c及びfortranに対応しているようだ しかも無料で。 これを使ってみることにした。
まずは導入。
最初 TDM−Fotranをインストールしたが、write文にエラーがあるそうで、実際にうまく動かないので、
minGM-w64-fortranをインストールし直した。

あらきけいすけの雑記帳 さんのページを参考に初期設定。
ソースファイルを置くフォルダーに mingw-w64.bat をコピー
openMPは基本的にDOループを明示的に並列化できるようだ。

機器の概略仕様は以下のとおり、詳しくはここ を見てください。
CPU:Intel Core i5 4570 3.2GHz    Memory:8GB    OS:Windows 7 Home   excel2013 vba

実行するといくつかの段階でエラーが出たのでメモ代わりに記録しておく。

1.エラーその1 

 コンパイルエラーにはならないが「アプリケーション実行時のソフトウェア例外(APPCRASH c00000fd)」のエラーがでた。 多数の素数(最大は約100万個)をファイルから配列に読み混む段階で出ているようだ。
ただし、read文よりも前にある最初の実行文が全く実行されずにエラーとなる。 エラーを調べてみる とスタックオーバーフローのようだ。指定範囲外にアクセスすると出るエラーとのこと
問題の署名:
  問題イベント名:	APPCRASH
  アプリケーション名:	openmp.exe
  アプリケーションのバージョン:	0.0.0.0
  アプリケーションのタイムスタンプ:	5a897892
  障害モジュールの名前:	openmp.exe
  障害モジュールのバージョン:	0.0.0.0
  障害モジュールのタイムスタンプ:	5a897892
  例外コード:	c00000fd
  例外オフセット:	00002b73
  OS バージョン:	6.1.7601.2.1.0.768.3
  ロケール ID:	1041
  追加情報 1:	f6fd
  追加情報 2:	f6fdff1be24bd4a93309a009b8de72f0
  追加情報 3:	e0a8
  追加情報 4:	e0a884d85aaa560a55bf7d0eec5fdd0a

自分のためのメモ さんのページを発見。
 
配列を最初に宣言せずに、実行時に配列を確保することでスタック外の別領域に確保されるとのこと。 配列宣言時はallocatableで宣言し、実行時にallocateすることで行えることを知った。 大昔(約40年前)とはかなり違う。
これで素数をファイルから読み込む時のエラーはなくなった。

2.エラーその2 

 実行結果がばらばら、素数の積の回文数はチェックしているようだが、 出力した値がばらばら。何のことか解らない。
調べていくと、doループを並列処理しているとき、何も指定しないと変数は共通となるとのこと doループの添え字(iとかj)は並列処理ごとに別に確保されるようだ。
そこで共通で使っている変数を並列処理(以下「スレッド」という)ごとに独立して確保するために、 do ループの並列処理指定( !$omp do )の行に「private(変数1,変数2,・・)」と 指定することでそれぞれスレッド毎に独立に変数が確保されることになるようだ。

それでもうまくいかない。

3.エラーその3 

 回文数となる数をカウントする「kosu」は共通で使う必要があり、かつ、それぞれが排他的に扱う必要がある。 このため、!$omp critical文をいれることにした。
単に合計をとるだけなら、reduction + 指定すれば良いのだが、 何個目の回文数かそのときの素数m、nを記録するために配列の引数として使用することから reductionではだめだった。
これでまともな結果がoutputされるようになった。

うまくいったソースファイルをここに乗せます。 この後に記述した、schedule文を入れたソースの一例です。


4.時間比較 

アルゴリズムもVBAから変更し数値を数値のまま逆転させて比較することにより、VBAで行っていた 文字列としての比較よりも高速になったようだ。
コンパイルすることでVBAより6倍くらい速くなり、アルゴリズムの改善+4coreによる並列実行(注1)により gfortranで実行した場合の約10倍程度速くなった。 表−1に計算時間を示す。

(2018/3/6追記) 注1: 表−1のgfortranの時間はCPU時間だった。

(2018/3/6追記ここまで)

表−1 vbaとgfortran及びopenmpを使った並列処理 時間比較
N番目の素数まで 3,000 10,000 20,000 30,000 70,000 100,000 200,000
回文となる組み合わせの数 1,064 2,937 5,362 8,066 15,668 20,363 41,807
変数m番目の最後 2,942 9,822 17,664 27,135 27,135 99,677 199,279
変数n番目の最後 2,960 9,978 17,672 27,193 27,193 99,847 199,970
vba 45.2 535.4 2106.8 5,054.6 27,430.5
時:分:秒 0:00:45 0:08:55 0:35:07 1:24:15 7:37:10
vba(改良) 24,268.3 55,419.9
時:分:秒 6:44:28 15:23:40
gfortran ディスプレイ出力 9.3 85.0 327.7 737.9 4,045.6
時:分:秒 0:00:09 0:01:25 0:05:28 0:12:18 1:07:26
gfortran ファイルに逐次出力 11.6 111.2 440.4 1,000.1
時:分:秒 0:00:12 0:01:51 0:07:20 0:16:40
gfortran ファイルにまとめて出力 11.4 106.8 423.0 10,731.4 42,509.2
時:分:秒 0:00:11 0:01:47 0:07:03 2:58:51 11:48:29
gfortran ファイルにまとめて出力※ 2.6 8.8 30.6 86.2 490.3 1,018.6 4,288.4
時:分:秒 0:00:03 0:00:09 0:00:31 0:01:26 0:08:10 0:16:59 1:11:28
※openmpによる並列処理及び回文数判断を文字列から数値に変更




その後、10万番目までの素数の積で回文数となる場合をチェックさせてみると、 cupが4つ共動いている状態(100%)から順次CPUが終了(75%→50%→25%→0%) していく。 CPUの負荷にばらつきがある。 対角項を含む三角行列を計算しているようなものだから(素数m番目<=素数n番目となる素数の積)、 均等にdoループをCPUに割り振ると 速く終わるものと最後まで稼働するものと差が出るようだ。 均等化するため、do文の並列化指定のなかに static、dyanamic,1000〜20000で 変化させてみた、多少staticよりは速くなるが、期待したほどではない。
guidedもためしたが、 余り差は無い。ループの数が少ないのだろうか 結果を表−2に示す。


表−2 openMPでgfortran使った schedule処理による 時間比較 スレッドは4つ
N番目の素数まで 30,000 70,000 100,000
回文となる組み合わせの数 8,066 15,668 20,363
変数m番目の最後 27,135 27,135 99,677
変数n番目の最後 27,193 27,193 99,847
openmp-17-1.f90 schedule( static ) 85.96
openmp-17-2.f90 schedule( dynamic ) 85.49
openmp-17-3.f90 schedule( dynamic,1000 ) 85.57
openmp-17-4.f90 schedule( dynamic,2000 ) 85.36
openmp-17-5.f90 schedule( dynamic,3000 ) 85.36
openmp-17-6.f90 schedule( dynamic,4000 ) 85.38
openmp-17-7.f90 schedule( dynamic,5000 ) 84.88
openmp-17-8.f90 schedule( dynamic,6000 ) 84.54
openmp-17-9.f90 schedule( dynamic,7000 ) 83.15
openmp-17-10.f90 schedule( dynamic,8000 ) 83.40
openmp-17-11.f90 schedule( dynamic,9000 ) 81.39
openmp-17-12.f90 schedule( dynamic,10000 ) 81.88
openmp-20.f90 schedule( static ) 487.04
openmp-20-1.f90 schedule( dynamic ) 492.39
openmp-20-2.f90 schedule( dynamic,5000 ) 491.89
openmp-20-3.f90 schedule( dynamic,8000 ) 492.39
openmp-20-4.f90 schedule( dynamic,10000 ) 490.75
openmp-20-5.f90 schedule( dynamic,12000 ) 489.25
openmp-20-6.f90 schedule( dynamic,15000 ) 484.20
openmp-20-7.f90 schedule( dynamic,17000 ) 480.08
openmp-30.f90 schedule( guided ) 485.37
openmp-40.f90 schedule( static ) 1014.83325
openmp-40-1.f90 schedule( dynamic ) 1019.80969
openmp-40-2.f90 schedule( dynamic, 10000 ) 1019.76294
openmp-40-3.f90 schedule( dynamic, 20000 ) 1003.88202


(2018/03/06 追記) 表−1のgfortranの時間と表−2の時間はcpu時間であった。実時間ではない。
思い切って素数40万番目まで計算してみた。 20万番目までの実績から5時間くらいと思っていて、3時間半くらい過ぎたところで見たらいつの間にか終わっていた。(後で再計測すると1時間15分程度だった)。時間を見ると18,000秒。約5時間。??? どういうことか?? 調べてみるとプログラムの中で時間計測に使っていたのは「 call cpu_time 」だった。これはcpuの稼働時間を計測するもので 実時間では無かった。改めて表−3に実時間とCPU時間を計測したものを前回と合わせて表示する。

表−3を見るとdynamicの無指定とdyanamic,1000ぐらいの実時間が短いようだ。

時間計測を修正したプログラムの一例をここに載せます。

表−3 openMPでgfortran使った schedule処理による 実時間とCPU時間 追加比較 スレッドは4つ
N番目の素数まで 70,000
回文となる組み合わせの数 15,668
変数m番目の最後 27,135
変数n番目の最後 27,193
前回 or 今回 前回 今回
schedule処理 プログラム名 cpu-time(秒) cpu-time(秒) 実時間(秒) プログラム名
schedule( static ) openmp-20.f90 487.04 486.864 208.48 openmp-21.f90
schedule( dynamic ) openmp-20-1.f90 492.39 490.592 124.536 openmp-21-1.f90
schedule( dynamic,1000 ) −−− −−− 490.966 124.52 openmp-21-11.f90
schedule( dynamic,3000 ) −−− −−− 489.999 126.002 openmp-21-12.f90
schedule( dynamic,5000 ) openmp-20-2.f90 491.89 489.828 128.482 openmp-21-2.f90
schedule( dynamic,8000 ) openmp-20-3.f90 492.39 493.884 126.938 openmp-21-3.f90
schedule( dynamic,10000 ) openmp-20-4.f90 490.75 491.029 127.671 openmp-21-4.f90
schedule( dynamic,12000 ) openmp-20-5.f90 489.25 487.753 143.115 openmp-21-5.f90
schedule( dynamic,15000 ) openmp-20-6.f90 484.2 484.508 173.817 openmp-21-6.f90
schedule( dynamic,17000 ) openmp-20-7.f90 480.08 484.336 203.862 openmp-21-7.f90
schedule( guided ) openmp-30.f90 485.37 486.692 208.916 openmp-31.f90
(2018/03/06 追記ここまで)



(2018/03/07 追記)
これまでのN番目の素数までの実行時間を表−4にまとめた。

表−4 openMPでgfortran使った schedule処理による 実時間とCPU時間比較(スレッドは4つ)
N番目の素数まで 70,000 100,000 200,000 400,000
回文となる組み合わせの数 15,668 20,363 41,807 83,949
変数m番目の最後 27,135 99,677 199,279 227,280
変数n番目の最後 27,193 99,847 199,970 227,966
プログラム名 openmp-21-11.f90 openmp-22-11.f90 openmp-23-11.f90 openmp-24-11.f90
schedele 処理 dynamic , 1000 dynamic , 2000 dynamic , 2000 dynamic , 2000
cpu time (秒) 491 1,031 4,333 18,169
cpu time (時:分:秒) 0:08:11 0:17:11 1:12:13 5:02:49
実時間 (秒) 125 260 1,087 4,555
実時間 (時:分:秒) 0:02:05 0:04:20 0:18:07 1:15:55
(2018/03/07 追記ここまで)


 


表−1 時間比較へ  表−2 schedule比較へ  表−3 CPUtimeと実時間比較へ

電子工作へ     HomePage     このページのトップ