Fortranでの配列の要素へのアクセス順序について
Fortranの配列では列優先でメモリに格納されるそうだ。
Fortranの多次元配列は列優先 (Column Major) です。(C/C++言語では行優先)例えば3行4列の2次元整数配列は integer a(3,4) のように宣言され、メモリ上には以下の順番で数値が格納されます。
a(1,1) a(2,1) a(3,1) a(1,2) a(2,2) a(3,2) a(1,3) a(2,3) a(3,3) a(1,4) a(2,4) a(3,4)Fortran 入門: 配列
ということで以下のプログラムを使って実際に比べてみた。使用したマシンはちょっと昔のiMac。
program ArrayTest implicit none integer :: a(1000, 1000) = 0 integer :: n = 1000 real :: s = 0 integer i, j, k do k = 1, 5000 do j = 1, n do i = 1, n a(i, j) = j - i s = a(i, j) end do end do end do print *, s end program ArrayTest
program ArrayTest2 implicit none integer :: a(1000, 1000) = 0 integer :: n = 1000 real :: s = 0 integer i, j, k do k = 1, 5000 do i = 1, n do j = 1, n a(i, j) = j - i s = a(i, j) end do end do end do print *, s end program ArrayTest2
2つとも1000×1000の2次元配列の端から端までアクセスするのを5000回繰り返すだけのプログラム。上のほうは列優先でアクセスし、下のほうは行優先でアクセスする。最後のprintは特に意味はない。ループが終わったことの目印になればいいかと思ったけど別にいらないような…
timeコマンドで時間を測った結果がこちら。
・列優先のほう
0. real 0m30.697s user 0m30.297s sys 0m0.070s
・行優先のほう
0. real 0m42.868s user 0m42.522s sys 0m0.145s
なるほど確かに列優先だ。
じゃあ3次元配列も同じような感じなのかしら?ってことで調べてみた。
program ArrayTest3D implicit none integer :: a(100, 100, 100) = 0 integer :: n = 100 real :: s = 0 integer i, j, k, iter do iter = 1, 2000 do k = 1, n do j = 1, n do i = 1, n a(i, j, k) = k - j - i s = a(i, j, k) end do end do end do end do print *, s end program ArrayTest3D
program ArrayTest3D2 implicit none integer :: a(100, 100, 100) = 0 integer :: n = 100 real :: s = 0 integer i, j, k, iter do iter = 1, 2000 do i = 1, n do j = 1, n do k = 1, n a(i, j, k) = k - j - i s = a(i, j, k) end do end do end do end do print *, s end program ArrayTest3D2
上のほうが列優先で、下のほうが行優先。i,j,kの組み合わせはほかにもあるけどとりあえずこの2つでやってみた。
結果はこちら。
・列優先のほう
-100. real 0m30.861s user 0m30.845s sys 0m0.012s
・行優先のほう
-100. real 0m34.517s user 0m34.498s sys 0m0.012s
2次元配列の時ほどではないけど、上のほうが速いことがわかる。
上のほうでのアクセス順序がi,j,kの組み合わせの中で一番速いのかな?
ということで、Fortranで配列をループで扱うときはアクセスする順番に気を付けましょうというお話でした…