PROGRAM main
IMPLICIT NONE
INTEGER nmax
PARAMETER(nmax=1000000000)
INTEGER i
REAL sum
sum=0.0e0
DO i=1,nmax
sum=sum+0.1e0
ENDDO
WRITE(6,200) sum
200 FORMAT('The answer is ',e15.8,'.')
END
|
PROGRAM main
IMPLICIT NONE
INTEGER nmax,nunitsize
PARAMETER(nmax=1000000000,nunitsize=10000)
INTEGER i,j,nunit,namari
REAL sum,psum,asum
sum=0.0e0
nunit=INT(nmax/nunitsize)
namari=MOD(nmax,nunitsize)
DO i=1,nunit
psum=0.0e0
DO j=1,nunitsize
psum=psum+0.1e0
ENDDO
sum=sum+psum
ENDDO
IF(namari/=0) THEN
asum=0.0e0
DO i=1,namari
asum=asum+0.1e0
ENDDO
sum=sum+asum
ENDIF
WRITE(6,200) sum
200 FORMAT('The answer is ',e15.8,'.')
END
|
PROGRAM main
IMPLICIT NONE
INCLUDE 'mpif.h'
INTEGER nmax
PARAMETER(nmax=1000000000)
INTEGER i,j,k,nunit,istart,iend,
& ierr,myrank,isize,
& istatus(MPI_STATUS_SIZE)
REAL sum,buf
CALL MPI_INIT(ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,isize,ierr)
nunit=INT(nmax/isize)
istart=nunit*myrank+1
iend=nunit*(myrank+1)
sum=0.0e0
IF(myrank==isize-1) THEN
DO i=istart,nmax
sum=sum+0.1e0
ENDDO
ELSE
DO i=istart,iend
sum=sum+0.1e0
ENDDO
ENDIF
IF(myrank==0) THEN
DO i=1,isize-1
CALL MPI_RECV(buf,1,MPI_REAL,i,MPI_ANY_TAG,
& MPI_COMM_WORLD,istatus,ierr)
sum=sum+buf
ENDDO
ELSE
buf=sum
CALL MPI_SEND(sum,1,MPI_REAL,0,1,
& MPI_COMM_WORLD,ierr)
ENDIF
IF(myrank==0) THEN
WRITE(6,200) sum
200 FORMAT('The answer is',e15.8,'.')
ENDIF
CALL MPI_FINALIZE(ierr)
END
|
PROGRAM main
IMPLICIT NONE
INCLUDE 'mpif.h'
INTEGER nmax,nunitsize
PARAMETER(nmax=1000000000,nunitsize=10000)
INTEGER i,j,ierr,myrank,isize,
& nunit,nunitamari,namari,istart,
& iend,istatus(MPI_STATUS_SIZE)
REAL sum,psum,asum,buf
CALL MPI_INIT(ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,isize,ierr)
nunit=INT(INT(nmax/nunitsize)/isize)
nunitamari=MOD(INT(nmax/nunitsize),isize)
namari=MOD(nmax,nunitsize)
istart=nunit*myrank+1
iend=nunit*(myrank+1)
sum=0.0e0
IF(myrank==isize-1) THEN
DO i=istart,iend
psum=0.0e0
DO j=1,nunitsize
psum=psum+0.1e0
ENDDO
sum=sum+psum
ENDDO
IF(namari/=0) THEN
asum=0.0e0
DO i=1,namari
asum=asum+0.1e0
ENDDO
sum=sum+asum
ENDIF
ELSE
IF(nunitamari>myrank) THEN
iend=iend+1
ENDIF
DO i=istart,iend
psum=0.0e0
DO j=1,nunitsize
psum=psum+0.1e0
ENDDO
sum=sum+psum
ENDDO
ENDIF
IF(myrank==0) THEN
DO i=1,isize-1
CALL MPI_RECV(buf,1,MPI_REAL,i,MPI_ANY_TAG,
& MPI_COMM_WORLD,istatus,ierr)
sum=sum+buf
ENDDO
ELSE
CALL MPI_SEND(sum,1,MPI_REAL,0,1,
& MPI_COMM_WORLD,ierr)
ENDIF
IF(myrank==0) THEN
WRITE(6,200) sum
200 FORMAT('The answer is',e15.8,'.')
ENDIF
CALL MPI_FINALIZE(ierr)
END
|
用いた並列計算機は、産業技術総合研究所先端計算機センター(TACC)に設置されている並列計算機Alta Technology AltaCluster 、Hitachi SR8000 、IBM RS/6000 SPおよびSGI Origin2000を用いた。計算機の詳細をTable 1に示した。なお、並列計算は1、2、4、8CPUを用いて行い、プログラムの実行時間は、MPIの初期化、ノード数およびランクの取得直後から終了処理の直前までの時間を5回計測し、最短時間を採用した。また、コンパイル時の最適化等のオプションは使用しなかった。
Table 1. Details of Computer Architecture
| AltaCluster | Hitach SR8000 | IBM RS/6000 SP | SGI Origin2000 | |
|---|---|---|---|---|
| CPU | Pentium III (katmai) | SR8000 | POWER3 | R12000 |
| CPU数 | 8 | 512 | 256 | 64 |
| クロック周波数(MHz) | 450 | 250 | 200 | 400 |
| メモリサイズ | 256MB/CPU | 8GB/8CPUs | 960MB/2CPUs | 2GB/CPU |
| 1次CachSize(データ+命令) | 16KB+16KB | 128KB+64KB | 64KB+32KB | 32KB+32KB |
| ネットワーク種 | ether (10Base-T) | 多次元クロスバー | SPスイッチ | NUMA SMP |
| Fortranコンパイラ名 | PGI Fortran | 最適化F90コンパイラ | XL Fortrran for AIX | MIPSpro F90 |
| Fortranコンパイラバージョン | 3.1 | 01-03 | 7.1 | 7.3 |
Table 2. Execution Time and Speed-up Ratio* in Number of Processors
| Number of Processors | ||||||||
|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 4 | 8 | |||||
| Execution time | Speed-up ratio | Execution time | Speed-up ratio | Execution time | Speed-up ratio | Execution time | Speed-up ratio | |
| sec | --- | sec | --- | sec | --- | sec | --- | |
| Alta Cluster | ||||||||
| Program 1 | 14.41 | 1 | 8.80 | 1.64 | 4.45 | 3.24 | 2.21 | 6.52 |
| Program 2 | 17.78 | 1 | 8.88 | 2.00 | 4.44 | 4.00 | 2.21 | 8.05 |
| SR-8000 | ||||||||
| Program 1 | 82.57 | 1 | 41.11 | 2.01 | 20.56 | 4.02 | 10.30 | 8.02 |
| Program 2 | 89.23 | 1 | 44.51 | 2.00 | 22.31 | 4.00 | 11.15 | 8.00 |
| RS/6000 SP | ||||||||
| Program 1 | 34.72 | 1 | 17.34 | 2.00 | 8.67 | 4.00 | 4.34 | 8.00 |
| Program 2 | 34.73 | 1 | 17.35 | 2.00 | 8.68 | 4.00 | 4.34 | 8.00 |
| Origin2000 | ||||||||
| Program 1 | 25.63 | 1 | 12.76 | 2.01 | 6.37 | 4.02 | 3.19 | 8.03 |
| Program 2 | 25.63 | 1 | 12.83 | 2.00 | 6.41 | 4.00 | 3.21 | 7.98 |

Figure 5. Execution Time and Speed-up Ratio in Number of Processors
実行速度は期待されるようにCPUに応じてほぼ理想的な高速化が図られた。これは、プログラム1、2共に計算量に対して通信量が非常に小さく、比較的低速なTCP/IPを利用するAltaClusterにおいてもMPIの通信オーバヘッドが問題にならないことおよび負荷分散がきちんとできているためである。
Table 3に2つのプログラムによる和の値および誤差率を示した。
Table 3. Calculated Value and Relative Error * in Number of Processors
| Number of Processors | ||||||||
|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 4 | 8 | |||||
| Calculated value | Rel. error* | Calculated value | Rel. error* | Calculated value | Rel. error* | Calculated value | Rel. error* | |
| --- | % | --- | % | --- | % | --- | % | |
| All computers | ||||||||
| Program 1 | 2.09715E+06 | -97.9 | 4.1943E+06 | -95.8 | 8.38861E+06 | -91.6 | 1.67772E+07 | -83.2 |
| Program 2 | 9.99998E+07 | -0.00025 | 9.99995E+07 | -0.00050 | 9.99990E+07 | -0.00098 | 9.99980E+07 | -0.00198 |
計算精度では、プログラム1および2においてそれぞれ4つの並列計算機で全く同じ結果が得られた。これは、現在製造されているほぼすべてのCPUは2進浮動小数点数の標準規格IEEE754に従っており、今回用いた計算機では浮動小数点数の取り扱いに違いがないために、結果が同一になった。また、コンパイラによる最適化を行わなかったことも、同一の結果を与えることに寄与している。
プログラム1は、非常に単純なプログラムであるにもかかわらず、良く知られているように全く正しい解を与えない。これは、“情報落ち”という誤差の累積によるものであることが広く知られている。Table 3をみると予想どおり並列処理を行うことによって情報落ちが少なくなるため、CPU数を増加させ処理を分散させることにより精度が向上しているが、8CPU程度では十分な計算誤差の回復には至っておらず、でたらめな答えとなっている。Figure 6 に示す回帰直線(y=-2.1x+100.0)から外装すると、50CPU程度を用いた並列処理を行うと正しい計算結果を与えるようになることが期待される。詳細な議論は行わないが、SGI Origin2000 60CPUまでのprogram 3の実行結果をFigure 7に示した。50CPU程度までは、直線的に精度が向上している。Figure 6の回帰直線からも示唆されるように50CPUよりも少ないところで正しい値(1.0E+08)となり、さらに用いるCPU数が60程度に大きくなるとまたエラーが増大するが、50CPU程度までにみられるような直線的増加ではない。

Figure 6. Relative Error (%) by Programs 1 and 2

Figure 7. Calculated Values of Program 1 on SGI Origin 2000
プログラム1とプログラム2は原理的に同一の解を与えるはずであるが、数値的には全く異なる解を与える。この例は、プログラムの書き方によって計算結果が異なってくることの良い例となっている。
プログラム2は、部分和を取ることにより情報落ちを回避するプログラムとなっているためほぼ正しい解が得られている。しかし、Table 3およびFigure 6から判るように、並列処理を行うことで、非常にわずかであるが見かけ上精度が悪くなっており、並列分散処理を行うと精度が向上するという一般的な傾向とはずれている。これは、以下のような考察により理解できる。まず、1つのCPUにおいて104回の部分和が数回計算されるが、この時丸め誤差を含んだ数を加えたことによる誤差が蓄積され、例えば0.1の104回の部分和は、正確な値(1000.0)にはならず、999.902…と誤差を含んだ値をとる。部分和の和をとっていくごく初期の段階では、部分和の和と各部分和の大きさがそれほど違わないため、丸めに起因する部分和の段階での誤差が次第に蓄積していく。ところが、部分和の総和が、各部分和の絶対値に比べて非常に大きくなっていくと、部分和段階での演算誤差が丸めにより切り上げられ、偶然真値(1000.0)として加えられる。そのため部分和の和をとるごく初期の段階を除けば、部分和の和に誤差が蓄積しない。逆に並列処理を行うと部分和の和のごく初期にたまった誤差が和になって表れ、CPU数が増すことにより誤差が比例的に増す。したがって、プログラム1および2の並列実行時にみられた現象は、MPIによる並列処理に起因する誤差でなく、部分和の取り方の違いによるよる現象であると考えられる。これは、1CPUを用いてMPIでの並列処理による和の取り方をシミュレートすることにより全く同様の結果を得たことからも確かめられた。
この研究は静岡県職員資質向上研修として行ったものである。計算は独立行政法人産業技術総合研究所先端情報計算センター(TACC)で行われた。