Fortranでオブジェクト指向(8)
本日のポイント 構造体を使用した時の実行速度
当たり前だけど恐ろしきはキャッシュミス
構造体を使用した時の計算の実行速度を、構造体を使用しない
従来のコードを比較しました。
gfortranで比較した場合、同じ計算をすればほぼ同じ結果となりました。
生成される実行ファイルも(メモリ割り当てがことなる以外)バイナリレベルでほぼ同じです。
但しメモリ割り当てが変化するのでキャッシュを無視した
間違ったコードを書くと大きく異なります。
メモリ割り当てとキャッシュについて
構造体を使用した場合、連続したアドレスに構造体のメンバ変数が
割り当てられるので適切に設計された構造体ならキャッシュミスが
多少避けられるようです。
一方、構造体を使用しない場合は変数ごとにアドレスが異なるので、
変なコードを書くとそのミスがそのまま実行時間に反映するようです。
以下に今回使用したサンプルコードを示します。
サンプルコードは間違ったコードでループの順番がi,j,kの入れ子に
なっています。正しくはk,j,iで入れ子にしなければなりません。
正しく書いたコードではどちらも同じ実行速度になります。
間違ったコードでは構造体を使用した場合5倍程度時間がかかり
構造体を使用しない場合10倍程度時間がかかりました。
構造体使用
program bench_obj
implicit none
TYPE pt
double precision x, y, z
END TYPE pt
integer, parameter :: ni = 500, nj = 500, nk = 500
double precision, parameter :: xmax = 1.0d0, ymax = 1.0d0, zmax = 1.0d0
double precision rd
integer i, j, k
integer :: t1, t2, t3, t4
TYPE(pt), allocatable :: point(:,:,:)
rd = xmax / dble(ni)
call system_clock(t1)
allocate(point(ni, nj, nk))
call system_clock(t2)
! ループ順が本来はk, j, iになる
do i = 1, ni
do j = 1, nj
do k = 1, nk
point(i,j,k)%x = dble(i-1) * rd
point(i,j,k)%y = dble(j-1) * rd
point(i,j,k)%z = dble(k-1) * rd
end do
end do
end do
call system_clock(t3)
deallocate(point)
call system_clock(t4)
write(6,*) 'to allocate:', t2-t1, '[sec]'
write(6,*) 'to calculate:', t3-t2, '[sec]'
write(6,*) 'to deallocate:', t4-t3, '[sec]'
read(5,*)
stop
end
構造体使用しない版
program bench_conv
implicit none
integer, parameter :: ni = 500, nj = 500, nk = 500
double precision, parameter :: xmax = 1.0d0, ymax = 1.0d0, zmax = 1.0d0
double precision, allocatable :: x(:,:,:), y(:,:,:), z(:,:,:)
integer i, j, k
double precision rd
integer :: t1, t2, t3, t4
rd = 1.0d0 / dble(ni)
call system_clock(t1)
allocate(x(ni,nj,nk), y(ni,nj,nk), z(ni,nj,nk))
call system_clock(t2)
! ループ順が本来はk, j, iになる
do i = 1, ni
do j = 1, nj
do k = 1, nk
x(i,j,k) = dble(i-1) * rd
y(i,j,k) = dble(j-1) * rd
z(i,j,k) = dble(k-1) * rd
end do
end do
end do
call system_clock(t3)
deallocate(x, y, z)
call system_clock(t4)
write(6,*) "to allocate:", t2-t1, "[sec]"
write(6,*) "to calculate:", t3-t2, "[sec]"
write(6,*) "to deallocate:", t4-t3, "[sec]"
read(5,*)
stop
end