ソフテック・トップページへ
ホーム 製品 セキュリティ・サービス HPCサービス ダウンロード 企業情報

PGI compiler Tutorial
行列積のプログラムを例にPGIを使用してみる
PGI チュートリアル > 行列積プログラム

 
行列積のプログラムを例に PGI コンパイラを使用する

ここでは、行列積の計算を行うためのプログラムを例示し、PGI コンパイラの使用法ACML ライブラリの使用法行列積計算の性能等に関して説明します。ここで、例示したプログラムは、行列積の処理プログラムを以下の三つの方法で記述したものです。

  • 一般的な Fortran プログラムでコーディング例 (シリアル版と OpenMP 並列版)
  • F90 の組込み関数(MATMUL)を使用した例
  • AMD ACML ライブラリを使用する例 (シリアル版と OpenMP 並列版)


本プログラムは、Fortran 上でプリプロセス・ディレクティブを組込み、それぞれの機能プログラムがコンパイル・動作できるようになっています。ここでは、これらを実現するための一連の処理の方法と実行性能について説明します。ソースファイルは、以下のものを使用します。

  使用ソースファイル : matmul.F (Fortran90ベース)
  時間計測ルーチン   : wallclock.c(下記で述べる second() 関数) 
  (Elapsed timeベース:並列処理時の時間計測に便利、CPU 時間ベースではない)


KEYWORD: プリプロセス処理、 行列積性能、 AMD ACML ライブラリ、 DGEMM、 DGEMM ベンチマーク
         OpenMP並列化、 F90組込み関数 MATMUL、 時間計測方法


Fortran でのプリプロセス処理

C 言語でのプリプロセス処理は、一般的に使用される方法ですが、Fortran ベースにおいても使用できます。基本的な構文は、C 言語対応のものに習いますが、よく使用されるものは #IF DEFINED 句であり、コンパイル・オプションで -D に定義された「フレーズ」をもとに真偽を判定し、翻訳すべきソースファイルを生成します。以下の例では、その選択肢のフレーズとして、「MATMUL」、「BLAS」、「指定しない」のパターンで判断され、プリプロセス後のソースが生成されます。

【プリプロセス処理を行うコンパイル・オプション】
プリプロセス処理を明示的にコンパイラに知らせるためには、ソースファイル名の接尾子を .f (.f90, f95) 形式ではなく、 .F (.F90, F95)形式に変更します。この形式のファイル名は、全てプリプロセス処理の対象のファイルとしてコンパイラは認識します。さらに「フレーズ」を指定するには、 C言語系と同じように、-D オプションに続く文字列で指定します。

 $ pgf95 -fastsse -Minfo -DBLAS matmul.F

#if defined MATMUL             (-DMATMUL が指定された場合、以下の文を採用)
! F90 Intrinsic function (MATMUL)
         c = matmul(a, b)

#elif defined BLAS       (-DBLAS が指定された場合、以下の文を採用)
! AMD ACML(LAPACK) Library
         call dgemm('N','N', m, p, n, 1.0d0, a, m, b, n, 0.0d0, c, n)
#else (-Dを指定しない場合、以下の文を採用) ! Source coding !$omp parallel default(shared) !$omp do schedule(runtime) do j = 1, p do i = 1, m c(i,j) = 0.0 enddo enddo do i = 1, m !$omp do schedule(runtime) do ii = 1, n arow(ii) = a(i,ii) enddo !$omp do schedule(runtime) do j = 1, p do k = 1, n c(i,j) = c(i,j) + arow(k) * b(k,j) enddo enddo enddo !$omp end parallel #endif

ソースファイルのプリプロセス処理後の編集された中間ソースファイルを作成するためには、以下のオプションも指定します。

 $ pgf95 -fastsse -Minfo -DBLAS -E matmul.F (プリプロセス処理後、編集された内容が標準出力に出力される)
 $ pgf95 -fastsse -Minfo -DBLAS -F matmul.F (プリプロセス処理後、編集された内容が matmul.f と言うファイルに出力される)

Elapsed time (経過時間)を測定する関数

プログラム中のある部分の実行時間を測定する場合、その対象部分の前後に時間計測ルーチンを挟んで、経過時間を測定しなければならないことがあります。ここでは、よく使用される時間関数について、説明します。一般に、1CPU(シリアル)計算の実行時間を測定する場合には、CPU時間測定の関数を使用しますが、並列処理の時間計測においては、複数のCPUが消費したCPU時間の総和ではなく、実際の経過時間を測り、性能向上の度合いを確かめる必要があります。並列処理性能は、経過時間(Elapsed time) をもとに判断する必要があるため、ここでは、その関数の使用法を説明します。この方法は、1CPU計算(シリアル計算)における時間計測でも使用できます。
ここで説明する時間計測の方法は、以下の三つです。その使用法については、matmul.F ファイルの内容を見てお確かめください。

関数名 時間精度 使用可能言語 対応 OS 説明
second マイクロ秒 F77/F90/C/C++ Linux システム時間関数 (gettimeofday) 使用
system_clock ミリ秒 F90 Linux/Windows Fortran90 の組み込み関数
omp_get_wtime マイクロ秒 F77/F90/C/C++ Linux/Windows OpenMPの組込み関数


  matmul.F 上では、-D マクロを指定しない場合使用される (wallclock.c を参照)
 ◆matmul.F 上では、-DSYSCLK を指定する場合使用される
  matmul.F 上では、-DOMPTIME を指定する場合使用される(PGI 6.1 以降で使用可能)
 
なお、FORTRAN77 のプログラムでは、△六藩僂任ないため、,△襪い廊をお利用ください。
例題の matmul.F 上では、以下のようなコーディングを行い、使用する時間関数のマクロ名を指定して選択している。

#if defined (SYSCLK)
! Elapsed time : Using F90 system_clock    : Milli-second
      call system_clock(count=clock0)

#elif defined (OMPTIME)                : Micro-second
! Elapsed time : OpenMP build-in function
      time0 = omp_get_wtime()
#else
! Elapsed time : SECOND() C function       : Micro-second
      time0 = second()
#endif

second() 関数を使用する際のプログラム上の設定

      real*8 second                                         second() 関数名も倍精度宣言
      real*8 time0, time1, time2                            一時的に時間を保存する変数も倍精度
      real*8 t, walltime

      time0=second()                             この時点での経過時間を取得
 
    {計算ブロック}

      time1=second()                             この時点での経過時間を取得

      walltime = (time1 - time0)              経過時間(秒単位)

second() 関数を含む wallclock.c の C 言語ファイルのオブジェクトを Fortran と結合するには、以下のコマンドで行います。PGCCコンパイラも使用できる環境であれば、pgf95 のコマンドライン上に C ファイルを指定すれば自動的に C のコンパイルも行います。

   $ pgf95 -fastsse -Minfo matmul.Fwallclock.c

もし、Fortran の言語コンパイラしかない場合は、 gcc を使用してコンパイルした後、pgf95 コマンドでリンクします。

   $ gcc -c wallclock.c
   $ pgf95 -fastsse -Minfo -matmul.Fwallclock.o

system_clock関数を使用する際のプログラム上の設定

      integer hz, clock0, clock1, clock2                   整数宣言(Integer*4 のみ)
      real*8 t, walltime

      call system_clock(count_rate=hz)         時間採取回数=時間精度(1000)
      print *, "system_clock resolution: ", real(1.d0/hz)

      call system_clock(count=clock0)         この時点でのクロックを取得
 
    {計算ブロック}

      call system_clock(count=clock1)         この時点でのクロックを取得

      t = (clock1 - clock0)
      walltime = real(t) / real(hz)          経過時間(秒単位)

OpenMP の omp_get_wtime 関数を使用する際のプログラム上の設定

      real*8 omp_get_wtime, omp_get_wtick               OpenMP 関数名を倍精度宣言

      print *, "OMP_Wtime resolution: ", omp_get_wtick()  時間関数の精度を表示

      time0 = omp_get_wtime()                   この時点での経過時間を取得
 
    {計算ブロック}

      time1 = omp_get_wtime()                   この時点での経過時間を取得

      walltime = (time1 - time0)               経過時間(秒単位)

行列積計算の三つの方法

ここでは、行列積を計算するための方法として、以下の方法を採用した場合の実行性能を比較した結果を示します。上記に示した matmul.F プログラムを PGI 6.1 でコンパイルして得られた性能結果を 表 - 1 に示しました。また、各計算の実際のコマンド・ログは以下に示しました。なお、使用したシステムは、表 - 2 に示しましたが、メモリがノーブランド製品を使用していることによる性能の多少の低下が考えられますが、6.4GB/secのメモリ帯域を共有する「デュアルコア」の並列性能としては、非常によい値が出ています。
フリーで使用可能なAMD 社の ACML ライブラリを使用すると、7.3GFLOPS を記録し、これは、プロセッサのピーク性能の 83.4% の実効性能となり、AMD・ACML ライブラリの性能の高さがうかがえます。

表 - 1 行列積計算の性能 (1000 x 1000)
計算手法 -D マクロ 1コア性能
(MFLOPS)
2コア性能
(MFLOPS)
実効性能
対ピーク性能
Fortran プログラムでコーディング (OpenMP) 指定しない 592 960 10.9 %
F90組込み関数を使用する (1CPU計算のみ) MATMUL を指定 3067 - 34.8 %
ACMLライブラリ LAPACK の使用 BLAS を指定 3826 7347 83.4 %

表 - 2  64-bit AMD Athlon64X2 システムの仕様
AMD Dual Core プロセッサ
プロセッサ AMD Athlon64X2 4400+
(1プロセッサ/2コア)
Clock 2.2 GHz
最大浮動小数点性能(ピーク性能) 8.8GFLOPS
L2 cache 1MB + 1MB
マザーボード  MSI K8N Neo4 Ultra
使用メモリ(最大帯域) DDR400 PC3200 CL3 3-3-3 (6.4GB/s) 512MB x 2 枚(ノーブランド製品)
使用 OS SUSE 10.0 (2.6.13-15-smp)
使用コンパイラ PGI 6.1

行列積計算を OpenMP ソースコーディングした場合の性能

行列積の計算を Fortran ソースコーディングしたものを OpenMP で並列化します。その際の 1スレッド計算、2スレッド計算時の性能を求めます。

【ソース内容】

! Source coding
!$omp parallel default(shared)
!$omp do schedule(runtime)
         do j = 1, p
            do i = 1, m
               c(i,j) = 0.0
            enddo
         enddo
         do i = 1, m
!$omp do schedule(runtime)
            do ii = 1, n
               arow(ii) = a(i,ii)
            enddo
!$omp do schedule(runtime)
            do j = 1, p
               do k = 1, n
                  c(i,j) = c(i,j) + arow(k) * b(k,j)
               enddo
            enddo
         enddo
!$omp end parallel

------------------------------------------------------------------
$ pgf90 -fastsse -Minfo -mp=align -Mconcur wallclock.o matmul.F  
matmul.F:
matmul_time:
    52, Invariant assignments hoisted out of loop
matmul_time:
    29, Interchange produces reordered loop nest: 30, 29
        Generated vector sse code for inner loop
    34, Interchange produces reordered loop nest: 35, 34
        Generated vector sse code for inner loop
    64, Parallel region activated
    66, Parallel loop activated; runtime schedule iteration allocation
    71, Barrier
    73, Parallel loop activated; runtime schedule iteration allocation
        Generated vector sse code for inner loop
    76, Barrier
    77, Parallel loop activated; runtime schedule iteration allocation
    78, Generated an alternate loop for the inner loop
        Generated vector sse code for inner loop
        Generated 2 prefetch instructions for this loop
        Generated vector sse code for inner loop
        Generated 2 prefetch instructions for this loop
    82, Barrier
        Parallel region terminated

$ export OMP_NUM_THREADS=1 (1 スレッド)
$ a.out
 Elapsed time =    3.375158071517944       (sec)
 M =         1000 , N =         1000 , P =         1000
 MFLOPS =     592.2685568030208
 c(1,1) =     1000.000000000000

$ export OMP_SCHEDULE="GUIDED,20" (OpenMP ランタイムスケジューリング)
$ export OMP_NUM_THREADS=2 (2 スレッド)
$ a.out
 Elapsed time =    2.081084966659546       (sec)
 M =         1000 , N =         1000 , P =         1000
 MFLOPS =     960.5566481068264
 c(1,1) =     1000.000000000000

F90組込み関数 MATMUL を使用した場合の性能(1CPUのみ)

【ソース内容】

! F90 Intrinsic function (MATMUL)
         c = matmul(a, b)

---------------------------------------------------------------
$ pgf90 -fastsse  -Minfo wallclock.o matmul.F -tp x64 -DMATMUL  
matmul.F:
matmul_time:
    29, Interchange produces reordered loop nest: 30, 29
        Generated vector sse code for inner loop
    34, Interchange produces reordered loop nest: 35, 34
        Generated vector sse code for inner loop
$ a.out
 Elapsed time =   0.6522284746170044       (sec)
 M =         1000 , N =         1000 , P =         1000
 MFLOPS =     3064.876922421755
 c(1,1) =     1000.000000000000

AMD社の ACML ライブラリ/LAPACK パッケージの DGEMM ルーチンを使用した場合の性能

ここでは、AMD 社が提供している OpenMP 版の ACML ライブラリを使用して並列性能の測定を行います。PGIコンパイラには、シリアル計算対応の ACML ライブラリはバンドルしており、自動的にインストールされますが、OpenMP 対応版は、別途、ソフトウェアをダウンロードして、インストールする必要があります。(PGI 6.2以降では、OpenMP版も PGI コンパイラにバンドルされました。) この詳細は、こちらをご覧ください

【ソース内容】

! AMD ACML(LAPACK) Library
         call dgemm('N','N', m, p, n, 1.0d0, a, m, b, n, 0.0d0, c, n)

--------------------------------------------------------------------------------------
$ pgf90 -fastsse -Minfo -mp matmul.F -tp x64 -DBLAS wallclock.o (次行に続く) 
  /export/acml310-64/pgi64_mp/lib/libacml.a (ACML ライブラリを指定する)
matmul.F:
matmul_time:
    29, Interchange produces reordered loop nest: 30, 29
        Generated vector sse code for inner loop
    30, Parallel code for non-innermost loop generated; block distribution
    34, Interchange produces reordered loop nest: 35, 34
        Generated vector sse code for inner loop
    35, Parallel code for non-innermost loop generated; block distribution

PGI 6.2においては、コンパイラに付属したOpenMP 対応 ACMLをリンクすることが可能です。
-mp と -lacml オプションを付加した場合、OpenMP 対応の ACMLを参照するようになります。
なお、PGI 7.0 以降の場合、OenMP対応の ACML は、明示的に -lacml_mp を指定することが必要です。

(PGI 6.2の場合 : Linux & Windows 64bit)
$ pgf90 -fastsse -Minfo -mp wallclock.o matmul.F -tp x64 -DBLAS -lacml  

(PGI 7.0以降の場合 : Linx & Windows)
$ pgf90 -fastsse -Minfo -mp wallclock.o matmul.F -tp x64 -DBLAS -lacml_mp  

(Windows 32ビット版の場合は、-Munix を付加する)
$ pgf90 -fastsse -Minfo  -mp -Munix wallclock.o matmul.F -DBLAS -lacml_mp  

$ export OMP_NUM_THREADS=1 (1 スレッド)
$ a.out
 Elapsed time =   0.5225369930267334       (sec)
 M =         1000 , N =         1000 , P =         1000
 MFLOPS =     3825.566470272335
 c(1,1) =     1000.000000000000

$ export OMP_NUM_THREADS=2 (2 スレッド)
$ a.out
 Elapsed time =   0.2720600366592407       (sec)
 M =         1000 , N =         1000 , P =         1000
 MFLOPS =     7347.642912008343
 c(1,1) =     1000.000000000000


ACMLライブラリの DGEMM (行列積)の性能

AMD 社の ACML ライブラリ性能を検証するため、DGEMM の行列サイズを変更して、実効性能を測定した結果を表 - 4 に示します。行列サイズが大きな場合、ピーク性能の 91% の実効性能を提供することから、このライブラリが優れていることが分かります。ATLAS 等のライブラリを使用するよりも、ACML の LAPACK ライブラリを使用することをお勧めします。

表 - 4 AGCM-DGEMM 行列積計算の性能 (Athlon64 X2 : 2.2GHz)
Matrix Size 100 300 500 800 1000 2000 3000 4000
1スレッド (MFLOPS) 3248 3505 3777 3888 3826 3976 4035 4060.
2スレッド (MFLOPS) 5904 5193 6888 7399 7347 7836 7990 8009
実効性能対ピーク性能 (%) 67% 59% 43% 44% 83% 89% 91% 91%

この画面トップへ

技術情報 TIPS トップに戻る >>




 ソフテックは、PGI 製品の公認正規代理店です

サイトマップ お問合せ
Copyright 2004-2006 SofTek Systems Inc. All Rights Reserved.