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で配列をループで扱うときはアクセスする順番に気を付けましょうというお話でした…