fortran66のブログ

fortran について書きます。

コンパイラ速さ比べ Windows 用 Fortran 三世代

年末なので暇つぶしに WindowsFortran 三世代で速さ比べをしてみます。用いたコンパイラは、Intel Fortran Ver.16.0 と Compaq Visual Fortran Ver.6.6C と Microsoft Fortran Powerstation Ver.4.0 の三つです。

簡単な CG 法のプログラムで 2000x2000 の密行列の線型方程式を解いて、計算時間を比較してみます。それぞれ実行速度優先の optimization をかけています。

実行画面出力 CPU Intel Core i7-4770@3.4GHz

2015-12-30 02:56:51.7 cpu_time = 0.00 (sec) ::make matrix
2015-12-30 02:56:51.7 cpu_time = 0.312E-01(sec) ::solve Ax = b
difference (rms) = 5.372769248802961E-007
2015-12-30 02:56:54.8 cpu_time = 3.11 (sec) ::normal end
続行するには何かキーを押してください . . .

2015-12-30 02:20:34.7 cpu_time = 0.00 (sec) ::make matrix
2015-12-30 02:20:34.7 cpu_time = 0.313E-01(sec) ::solve Ax = b
difference (rms) = 6.402642212039837E-007
2015-12-30 02:20:45.4 cpu_time = 10.8 (sec) ::normal end
Press any key to continue

015-12-30 02:20:49.2 cpu_time = .000 (sec) ::make matrix
015-12-30 02:20:49.4 cpu_time = .220 (sec) ::solve Ax = b
difference (rms) = 6.670762952641053E-007
015-12-30 02:21:37.0 cpu_time = 47.9 (sec) ::normal end
Press any key to continue

実行結果

実行時間は IF16.0, CVF6.6C, MSFPS4.0 それぞれで 3.11(sec) : 10.8(sec) : 47.9(sec) となっています。計算時間は、乱数で与えた初期値にも依存するので、三桁も有効桁は無く 10% くらいの幅があります。

最新版のコンパイラと20年前の物との間で、オーダー1桁のスピード差が出ています。なお、それぞれのコンパイラの対応している CPU は、世代的に AVX2/Xeon Phi, Pnetium4/AMD Athlon, 無印Pentium となっています。(ちなみに MSFPS1.0 では 486 まで、MS-FORTRAN5.1 では 286 までの対応となっています。)

コンパイラも新しいものが欲しいですね。

実行プログラム

IntelCompaq は、Fortran95 対応ですが、MS は発売時期的に Fortran90 のみ対応で、 subroutine cpu_time が無いので、ベンダー固有の function timef を使用しました。





module m_stamp
  ! use portlib ! MS-FPS4.0
  implicit none
  logical :: first = .true.
  real(kind(0.0d0)) :: t0, t1
contains
  subroutine stamp(text) ! print time stamp
    character(len = *), intent(in) :: text
    character(len =  8) :: date
    character(len = 10) :: time
    if (first) then 
      first = .false.
      call cpu_time(t0) ! t0 = timef() ! MS-FPS4.0
    end if 
    call cpu_time(t1)   ! t1 = timef() ! MS-FPS4.0
    call date_and_time(date, time)
    print '(6a, 6a, a, g15.3, 2a)', &
        date(1:4), '-', date(5:6), '-', date(7:8), '  ', &
        time(1:2), ':', time(3:4), ':', time(5:8), '  ', &
        ' cpu_time =', t1 - t0, '(sec) ::', text 
  end subroutine stamp  
end module m_stamp    
!===================================================
module m_cg
  implicit none
  integer, parameter :: kd = kind(0.0d0) !double precision
contains
  subroutine cg(a, b, x) ! conjugate gradient method
    real(kd), intent(in) :: a(:, :), b(:)
    real(kd), intent(in out) :: x(:)
    real(kd) :: alpha, beta, r2n, r2d, eb2, p(size(b)), q(size(b)), r(size(b))
    integer :: i
    r = b - matmul(a, x) 
    p = r
    r2n = dot_product(r, r)
    eb2 = epsilon(b) * dot_product(b, b)
    do i = 1, size(b)
      if ( r2n < eb2 ) exit
      r2d = r2n
      q = matmul(a, p)
      alpha = dot_product(p, r) / dot_product(p, q)
      x = x + alpha * p
      r = r - alpha * q
      r2n = dot_product(r, r)
      beta = r2n / r2d
      p = r + beta * p			   
    end do
  end subroutine cg
end module m_cg
!===================================================
program cg_main
  use m_cg
  use m_stamp
  implicit none
  integer, parameter :: ns = 2000 
  real(kd) :: a(ns, ns), b(ns), x(ns)
  integer :: i, j
  call stamp('make matrix')
  do i = 1, ns
    b(i) = 1.0_kd
    do j = i, ns ! givens matrix,  x = (1, 0, 0, ... , 0)t
      a(i, j) = real( 2 * min(i, j) - 1, kd )  
      a(j, i) = a(i, j)
    end do
  end do
  call stamp('solve Ax = b')
! 
  call random_seed() 
  call random_number(x) ! starting vector
  call cg(a, b, x)     
  x = matmul(a, x) - b
  print *, 'difference (rms) =', sqrt( dot_product(x, x) )
  call stamp('normal end')
end program cg_main