
【C&Fortran演習で学ぶ数値計算(3)】Fortranでの離れたメモリアクセスは実行速度が落ちる
前回の記事の後半でFortranで配列要素へのアクセスの仕方には気を付けましょうというお話を少しだけ触れました。
今回は、実際にどれくらい計算時間に違ってくるのか計測したので記事にしておきます。
まずは前回のおさらい。
C言語とFortranの違い
学生時代にはじめてFortranを触って以来ずっとFortranに慣れてきたので、そういえば配列要素を作成したときにFortranのindexは1から開始するということを忘れていました。
また、C言語とFortranでメモリの並び順も違うんですよね。
C言語の場合、2次元配列array[4][4]を作成するとメモリが連続するのは、array[0][0],array[0][1],array[0][2],array[0][3],array[1][0]・・・・
だから連続するメモリアクセスを行うにあたって、C言語は以下のようにfor文を回せばよいのですが・・・・
for (i = 0; i < N; i++){
for ( j = 0; j < N; j++){
printf("%5.2f",A[i][j]);
}
}
Fortranの場合、2次元配列array(4,4)を作成するとメモリが連続するのは、array(1,1),array(2,1),array(3,1),array(4,1),array(1,2)・・・・
だから連続するメモリアクセスを行うにあたって、Fortranは以下のようにdo文を回さなくてはならない・・・・
do j=1, N
do i=1,N
write(*, fmt='(1x, f5.2)', advance='no') A(i,j)
end do
end do
不連続なメモリアクセスは実行速度を落とすのでFortranを使う場合は注意が必要ですね。
こちらの参考書を参考にコードを書いています。
Fortranのプログラム
配列要素へのアクセスは以下のようにしたくなりますよね。
do i = 1, n
do j =1, n
a(i,j) = 10* i + j
write(*,*)i,j,"",a(i,j)
end do
end do
しかし、Fortranの配列の並び順がarray(1,1),array(2,1),array(3,1),array(4,1),array(1,2)・・・・であるため以下のように1つ目の要素から順番にアクセスをしていった方が効率が良いプログラムになります。
do j = 1, n
do i =1, n
a(i,j) = 10* i + j
write(*,*)i,j,"",a(i,j)
end do
end do
では、実際にCPUでの実行速度を計測してみましょう。
program main
implicit none
integer, allocatable ::a(:,:)
integer, parameter::n=2
integer::i, j
real(kind=8)::t1, t2
allocate(a(n,n))
write(*,*)"========================"
write(*,*)"離れたメモリでアクセス"
! 行列の作成
call cpu_time(t1)
do i = 1, n
do j =1, n
a(i,j) = 10* i + j
write(*,*)i,j,"",a(i,j)
end do
end do
call cpu_time(t2)
write(*,*)"CPU time = ", t2 - t1
! 全要素を出力
write(*,*)"全要素の出力"
write(*,*)a
write(*,*)"========================"
write(*,*)"メモリの並び順でアクセス"
! 行列の作成
call cpu_time(t1)
do j = 1, n
do i =1, n
a(i,j) = 10* i + j
write(*,*)i,j,"",a(i,j)
end do
end do
call cpu_time(t2)
write(*,*)"CPU time = ", t2 - t1
! 全要素を出力
write(*,*)"全要素の出力"
write(*,*)a
deallocate(a) ! メモリの解放
end program main
結果はこちらです。
$ gfortran F_list29.f90
$ ./a.out
========================
離れたメモリでアクセス
1 1 11
1 2 12
2 1 21
2 2 22
CPU time = 1.0399999999999993E-004
全要素の出力
11 21 12 22
========================
メモリの並び順でアクセス
1 1 11
2 1 21
1 2 12
2 2 22
CPU time = 3.7999999999999839E-005
全要素の出力
11 21 12 22
まずwrite(*,*)aで出力した結果が 11 21 12 22となっていることに注意します。
これはarray(1,1),array(2,1),array(1,2),array(2,2)の並び順で配列ができていることを意味しています。
そして結果について、
離れたメモリでアクセスした場合は 、
CPU time = 1.0399999999999993E-004となっています。
一方でメモリの並び順でアクセスした場合は、
CPU time = 3.7999999999999839E-005となり、計算時間は1/3になっています。
今回のような小さな配列では大した差は出てこないですが、3次元配列、4次元配列となるにつれて離れたメモリへのアクセスは実行速度を落とすことになります。
引き続き「C&Fortran演習で学ぶ数値計算」の勉強を進めます。
Twitter➡@t_kun_kamakiri
Instagram➡kamakiri1225
youtube➡https://www.youtube.com/channel/UCbG6_Q9ZRqqVT6YZOpcjDlQ
ブログ➡宇宙に入ったカマキリ(物理ブログ)
ココナラ➡物理の質問サポートサービス
コミュニティ➡製造業ブロガー