4.6 NECスパコン

4.6.1 NEC SX-Aurora TSUBASA

NEC SX-Aurora TSUBASA [5](以下、SX)を用いた高速化について述べます。
SXは1台にベクトル長256のベクトル演算器を8コア持っています。
内側のループをコンパイラーの自動ベクトル化によってベクトル計算し、 外側のループをOpenMPによって並列計算します。
さらに複数のノードで計算するには領域分割を行いMPIを用いて並列計算します。
本プログラムは前節まででOpenMPとMPIによって並列化されていますので、SXに対応することは比較的容易です。
ただしベクトル化するために余分な配列が必要になり必要メモリーは12/5=2.4倍になります。

4.6.2 SXの計算時間(Vシステム)

表4-6-1にVシステム(A100-1)の1〜2ノードの計算時間を示します。
表より2ノードでは1ノードの2倍近く速くなることがわかります。 また2ノードでは使用可能なメモリーが2倍になり、より大きな問題が計算できるようになります。

表4-6-1 SXの計算時間 (FUCUSスパコンVシステム、()内は1ノードとの速度比)
ノード数ベンチマーク300ベンチマーク400ベンチマーク500ベンチマーク600
116.2秒 (1.0) 28.9秒 (1.0) 50.5秒 (1.0) 89.7秒 (1.0)
210.2秒 (1.58) 15.6秒 (1.85) 26.7秒 (1.89) 46.8秒 (1.92)

4.6.3 SXの計算時間(Wシステム)

表4-6-2にWシステム(A300-8)の1VEと8VEの計算時間を示します。
実行コマンドは以下の通りです。

$ ofd_ncc -n 8 data/benchmark/benchmark300.ofd (1VEのとき)
$ mpiexec -ve 0-7 -n 8 ofd_ncc_mpi -n 8 data/benchmark/benchmark300.ofd (8VEのとき)
表より8VEは1VEと比べて最大6.3倍速くなることがわかります。 また8VE数では使用可能なメモリーが8倍になり、より大きな問題が計算できるようになります。

表4-6-2 SXの計算時間 (FUCUSスパコンWシステム、()内は1VEとの速度比)
VE数ベンチマーク300ベンチマーク400ベンチマーク500ベンチマーク600
115.0秒 (1.0)28.1秒 (1.0)48.9秒 (1.0)89.8秒 (1.0)
8 2.8秒 (5.4) 5.5秒 (5.1) 7.9秒 (6.2)14.2秒 (6.3)

4.6.4 コンパイルメッセージ

リスト4-6-1に計算の主要部のコンパイルメッセージを示します。
コンパイルオプションに"-D_VECTOR"が必要です。
これから、一番外側のループがOpenMPで並列計算され、 一番内側のループがベクトル計算されることがわかります。

リスト4-6-1 計算の主要部(updateEx.c)のコンパイルメッセージ


$ cat -n updateEx.c
     1  #include "ofd.h"
     2
     3  void updateEx_f(void)
     4  {
     5          const int64_t kmin = kMin;
     6          const int64_t kmax = kMax;
     7
     8          int64_t i;
     9  #ifdef _OPENMP
    10  #pragma omp parallel for
    11  #endif
    12          for (        i = iMin; i <  iMax; i++) {
    13          for (int64_t j = jMin; j <= jMax; j++) {
    14                  int64_t n = NA(i, j, kMin);
    15                  int64_t n1 = n - Nj;
    16                  int64_t n2 = n - Nk;
    17                  float *ptr_ex = &Ex[n];
    18                  float *ptr_hy = &Hy[n];
    19                  float *ptr_hz = &Hz[n];
    20                  float *ptr_hym = &Hy[n2];
    21                  float *ptr_hzm = &Hz[n1];
    22                  float *ptr_k1ex = &K1Ex[n];
    23                  float *ptr_k2ex = &K2Ex[n];
    24                  float      ryn =  RYn[j];
    25                  float *ptr_rzn = &RZn[kMin];
    26                  for (int64_t k = kmin; k <= kmax; k++) {
    27                          *ptr_ex = *ptr_k1ex * (*ptr_ex)
    28                                  + *ptr_k2ex * (      ryn * (*ptr_hz - *ptr_hzm)
    29                                                - *ptr_rzn * (*ptr_hy - *ptr_hym));
    30                          ptr_ex++;
    31                          ptr_hy++;
    32                          ptr_hz++;
    33                          ptr_hym++;
    34                          ptr_hzm++;
    35                          ptr_k1ex++;
    36                          ptr_k2ex++;
    37                          ptr_rzn++;
    38                  }
    39          }
    40          }
    41  }
$ ncc -c -O2 -fopenmp -D_VECTOR -I../include -Wall -fdiag-vector=2 -fdiag-parallel=2 updateEx.c
ncc: par(1801): updateEx.c, line 10: Parallel routine generated.: updateEx_f$1
ncc: par(1803): updateEx.c, line 12: Parallelized by "for".
ncc: vec( 103): updateEx.c, line 13: Unvectorized loop.
ncc: vec( 113): updateEx.c, line 13: Overhead of loop division is too large.
ncc: opt(1097): updateEx.c, line 14: This statement prevents loop optimization.
ncc: vec( 101): updateEx.c, line 26: Vectorized loop.
ncc: vec( 128): updateEx.c, line 27: Fused multiply-add operation applied.