多重ループ: Sample 1: 共通項をループの外に(FORTRAN)

高速化プログラミング   
トップ  >  演算数と高速化  >  多重ループ  >  Sample 1: 共通項をループの外に(FORTRAN)

多重ループ: Sample 1: 共通項をループの外に(FORTRAN)

言語の変更:   C版

■ 概要

ここでは式(1)にしたがって配列rとθから2次元配列xの値を計算します。2次元配列だから、2重ループを使用して配列xを求めます。

x_ij=r_iθ_j           ... 式(1)

Code 1では式(1)を素直にコーディングしたものです。メイン計算では2つのループ、ループjとループi、を設けてその中に式(1)の右辺をそのまま計算しています。

Code 1のメイン計算をよく観察すると、cos(theta(j))はループjの中で不変であることが解かります。ループiの中でcos(theta(j))が不変なのに、N回も繰り返し計算されることになります。cosの計算は重い計算のなので、できるだけ減らしたいものです。Code 1は無駄に重い計算を繰り返しているので、非常に遅いプログラムになります。

Code 1を改良したのはCode 2です。ループiの中で計算されていたcos(theta(j))を、ループiの外に出して、一時変数dに記憶しておきます。その後ループiの中でこの一時変数dを使えば、Code 1とまったく同じ結果が得られるはず。違いはcos(theta(j))を1回で済むので、Code 1よりCode 2の方がはるかに高速に計算できるはずです。

ここで用いた高速化手法は実はダミー変数を使用の一種です。繰り返し使用されている値をダミー変数に入れておいて、その後はダミー変数の値を参照するから、その値の計算は1回にすることができます。

■ ソースコード


  ◆ Code 1   ◆ Code 2
 
      program main
      implicit none
      real*8, allocatable :: r(:), theta(:), x(:,:)
      real*8 time
      integer*8 time0, time1, dtime
      integer i, j, n
c
      n = 10000

c Initialization
      allocate(r(n), theta(n), x(n,n))
      do i=1,n
        r(i) = rand()
        theta(i) = rand()
      end do

c Start time
      call system_clock(time0)

c Main calculation
      do j=1,n
c
        do i=1,n
          x(i,j) = r(i) * cos(theta(j))
        end do
      end do

c Finish time
      call system_clock(time1, dtime)

c Output time
      time = 1d0*(time1-time0)/dtime
      write(*,"(a7,f16.7)")"Time = ",time

      deallocate(r, theta, x)
      end program

    
 
      program main
      implicit none
      real*8, allocatable :: r(:), theta(:), x(:,:)
      real*8 time, d
      integer*8 time0, time1, dtime
      integer i, j, n
c
      n = 10000

c Initialization
      allocate(r(n), theta(n), x(n,n))
      do i=1,n
        r(i) = rand()
        theta(i) = rand()
      end do

c Start time
      call system_clock(time0)

c Main calculation
      do j=1,n
        d = cos(theta(j))
        do i=1,n
          x(i,j) = r(i) * d
        end do
      end do

c Finish time
      call system_clock(time1, dtime)

c Output time
      time = 1d0*(time1-time0)/dtime
      write(*,"(a7,f16.7)")"Time = ",time

      deallocate(r, theta, x)
      end program

    

■ 計算時間の測定結果

Code 1Code 2の計算時間の測定結果を表1に示します。ここではそれぞれのコードを5回実行して、平均とCode 1Code 2との計算時間の比率も表示します。

表1 計算時間の測定結果(単位: sec)
1回目 2回目 3回目 4回目 5回目 平均 倍率
Code 1 4.56 4.59 4.54 4.52 4.56 4.55 8.63
Code 2 0.51 0.55 0.53 0.51 0.53 0.53 -

■ 考察

Code 2Code 1に比べて8倍以上速いという結果が得ました。配列のサイズNが大きければさらに倍率が大きくなるでしょう。



はじめに

演算数を減らす

メモリジャンプを減らす

高性能のアルゴリズム

その他



5 0 0 3 6 8