久しぶりにやると忘れているのでメモ.
OpenMP
によるdo
ループの並列化の例.
コンパイラは Intel Fortran Compiler (19.0.4.243).
program sample
!$ use omp_lib
implicit none
!$ integer,parameter :: wp = selected_real_kind(p=15) !倍精度
integer::i
!$ integer::parallel_threads
!$ real(wp)::start_time,end_time
!$ parallel_threads = 3
!$ call omp_set_num_threads(parallel_threads)
!$ write(6,*)
!$ write(6,*) parallel_threads,"人でがんばります."
!$ write(6,*)
!$ start_time = omp_get_wtime()
!$OMP parallel do default(none) private(i)
do i = 1,6,1
write(6,'(A,I3,A)') "私は ",i," 番目のジョブを処理します!"
end do
!$OMP end parallel do
!$ end_time = omp_get_wtime()
!$ write(6,*)
!$ write(6,*) "経過時間 [sec]:",end_time - start_time
end program
上記のFortranプログラムをビルド&実行してみると次のようになる.
いつも通りビルド&実行:
$ ifort OpenMP_test.f90
$ ./a.out
私は 1 番目のジョブを処理します!
私は 2 番目のジョブを処理します!
私は 3 番目のジョブを処理します!
私は 4 番目のジョブを処理します!
私は 5 番目のジョブを処理します!
私は 6 番目のジョブを処理します!
!$
が構文通りコメント処理され,do
ループは逐次実行されている.
今度はqopenmp
オプションをつけてビルド&実行:
$ ifort OpenMP_test.f90 -qopenmp
$ ./a.out
3 人でがんばります.
私は 1 番目のジョブを処理します!
私は 2 番目のジョブを処理します!
私は 3 番目のジョブを処理します!
私は 5 番目のジョブを処理します!
私は 4 番目のジョブを処理します!
私は 6 番目のジョブを処理します!
経過時間 [sec]: 4.745960235595703E-003
!$
行が処理され,do
ループに並列化 (3スレッド) が効いている.
このため,上記のように実行順序が保証されなくなる.
do
ループの中でa,b,c,x,y,z
の変数を使っており,
x,y,z
は全スレッドで共有,
a,b,c
は各スレッドでの局所変数であるときは
!$omp parallel do default(none) private(a,b,c) shared(x,y,z)
と書く (参考).
巨大な配列をshared
するとオーバヘッドが大きくなり,
並列化効率が落ちるので注意.
また,計算負荷がi
に大きく依存しているときは,次のようにしてジョブの動的割付を検討する:
!$OMP parallel do schedule(dynamic) default(none)...
こうすると,ジョブが終わったコアが新たにジョブを受け付けて計算を始める.
デフォルトのschedule
オプションはシステムに依存するらしいが,
もしそれがschedule(static)
であった場合,
自分のジョブが終わると他のコアのジョブが終わるまで休むことになる (top
コマンドからCPU使用率を眺めていると休んでいるのがわかる).
なお,qopenmp
オプションはopenmp
の後続オプションであり,
コンパイラが古い場合はifort -openmp
とする.