XcalableMP入門
中尾 昌広（理化学研究所 計算科学研究センター）

第1回 HPC-Phys勉強会@京都大学基礎物理学研究所, 2018年8月22日

もくじ
XcalableMP（XMP）の概要説明
XMPプログラミング
Lattice QCDの実装!2

XMP誕生の背景
分散メモリシステムに対するプログラミングにはMPIが一般的
MPIはライブラリベース
プログラミングコストが大きい
目標
高性能と高生産性を兼ね備えた並列プログラミング言語の開発!3

XMPの概要（1/2）
PCクラスタコンソーシアムのXMP規格部会で仕様策定
MPIに代わる並列プログラミングモデル
FortranとCに対して，指示文とCoarray記法を用いた並列拡張
指示文なので，既存の逐次コードからの移行が容易
C++にも対応中MPIとの連携機能もあるので，既存のMPIコードの部分的なXMP化も可能（OpenMPとの連携も可能）

http://xcalablemp.org!4

XMPの概要（2/2）
SPMD (Single Program Multiple Data)
MPIと同じ各実行主体（XMPではノードと呼称，MPIのランクのようなもの）が同じコードを独立に重複して実行する
通信は指示文やCoarray記法によって明示的に行われる通信箇所が明確なので，性能チューニングが容易（HPFの教訓）
2つのメモリモデル
指示文によるグローバルビュー
Coarray記法によるローカルビュー!5

もくじ
XcalableMP（XMP）の概要説明
XMPプログラミング
Lattice QCDの実装!6

グローバルビュープログラミング
グローバルビュープログラミングは「解くべき問題全体を記述し，それを各ノードが分担する方法を示す」
例：問題1〜100を4人で分担して解け
ローカルビュープログラミングは「各ノードが解くべき問題を個別に示す」（MPIはローカルビュー）
例：ノード1は問題1〜25を解け．ノード2は……
基本的に指示文を挿入するだけなので，並列化は簡易
グローバルビューのための指示文
データマッピング：分散配列の定義など
ワークマッピング：Loop文の分割など
通信・同期
!7

Example of XMP programming in C
int a[MAX], sum = 0;
#pragma xmp nodes p[*]#pragma xmp template t[MAX]

分散配列の定義．配列a[]を複数ノードにブロック分割する#pragma xmp distribute t[block] onto p#pragma xmp align a[i] with t[i]:#pragma xmp loop on t[i] reduction(+:sum)for(int i = 0; i <MAX; i++){a[i] = foo(i);ループ文の分割と集約演算

sum += a[i];}ほぼ同じ指示文をCとFortranに提供

8

Example of XMP programming in F
integer :: a(MAX), sum = 0!$xmp nodes p(*)!$xmp template t(MAX)

分散配列の定義．配列a[]を複数ノードにブロック分割する!$xmp distribute t(block) onto p!$xmp align a(i) with t(i):!$xmp loop on t(i) reduction(+:sum)do i = 1, MAXa(i) = foo(i)ループ文の分割と集約演算

sum = sum + a(i)enddo

ほぼ同じ指示文をCとFortranに提供

9

XMPとMPIとの比較
XMP

MPI

int array[MAX], res = 0;
#pragma xmp nodes p[*]#pragma xmp template t[MAX]#pragma xmp distribute t[block] onto p#pragma xmp align array[i] with t[i]int array[MAX], res = 0;
int main(int argc, char **argv){
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

int main(){#pragma xmp loop on t[i] reduction(+:res)for (int i = 0; i < MAX; i++){array[i] = func(i);res += array[i];}return 0;}

int dx = MAX/size;
in llimit = rank * dx;
int ulimit = (rank != (size -1))? llimit + dx : MAX;
int temp_res = 0;
for(int i = llimit; i < ulimit; i++){array[i] = func(i);temp_res += array[i];}MPI_Allreduce(&temp_res, &res, 1, MPI_INT,MPI_SUM, MPI_COMM_WORLD);

少しの手間で、逐次イメージを残したまま、並列化できる

MPI_Finalize();
return 0;}!10loop指示文
loop指示文はfor文の直前に挿入する#pragma xmp loop on t[i]for(int i=0;i<16;i++){#pragma xmp nodes p[4]#pragma xmp template t[16]#pragma xmp distribute t[block] onto p
int a[16];#pragma xmp align a[i] with t[i]0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
a[16]
p[0]
p[1]
p[2]
p[3]
各ノードは赤いセルを並列に処理する．
!11

loop指示文
loop指示文はfor文の直前に挿入する#pragma xmp loop on t[i]for(int i=2;i<11;i++){#pragma#pragma xmp
xmp nodes
nodes p[4]p[4]#pragma#pragma xmp
xmp template
template t[16]t[16]#pragma#pragma xmp
xmp distributedistribute t[block]t[block] onto
onto ppint
a[16];#pragma
xmp align a[i] with t[i]#pragma xmp align a[i] with t[i]0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
a[16]
p[0]
p[1]
p[2]
p[3]
各ノードは赤いセルを並列に処理する．
!12

多次元のloop指示文
[C]

[F]

#pragma xmp nodes p[4][2]#pragma xmp template t[20][20]#pragma xmp distribute t[block][block] onto p
int a[20][20];#pragma xmp align a[i][j] with t[i][j]!$xmp nodes p(2,4)!$xmp template t(20,20)!$xmp distribute t(block,block) onto p
integer :: a(20,20);!$xmp align a(j,i) with t(j,i)#pragma xmp loop on t[i][j]for(int i=0;i<20;i++){for(int j=0;j<20;j++){a[i][j] = func(i, j);}}!$xmp loop on t(j, i)
do i=1, 20
do j=1, 20
a(j,i) = func(j,i)
end doend do!13

XMP指示文 + OpenMP指示文[C]#pragma xmp loop on t[i]#pragma omp parallel for
for(int i=0;i<20;i++){a[i] = i;}[C]#pragma omp parallel for#pragma xmp loop on t[i]for(int i=0;i<20;i++){a[i] = i;}[F]!$xmp loop on t(i)!$omp parallel dodo i=1, 20
a(i) = iend do!$omp end parallel do[F]!$omp parallel do!$xmp loop on t(i)do i=1, 20
a(i) = iend do!$omp end parallel do

指示文の順序は，どちらが先でも構わない!14bcast指示文とreduction指示文
bcast指示文
全ノードに任意のローカル変数を放送する（MPI̲Bcastに相当）[C]#pragma xmp bcast (b)#pragma xmp bcast (b) from p[2][F]!$xmp bcast (b)!$xmp bcast (b) from p(3)reduction指示文
ノード間で集約処理を行う（MPI̲Allreduceに相当）[C] #pragma xmp reduction (+:b)[F] !$xmp reduction (+:b)!15

gmove指示文
分散配列に対する通信
分散配列のある要素がどのノードが持っているかを知らなくてよい#pragma xmp gmove [C]a[2:4] = b[3:4];

array-name[ base : length ];0 1 2 3 4 5 6 7a[8]

0 1 2 3 4 5 6 7
b[8]

p[0]
p[1]
p[2]
p[3]
!16

gmove指示文
分散配列に対する通信
分散配列のある要素がどのノードが持っているかを知らなくてよい!$xmp gmovea(3:6) = b(4:7)[F]array-name( base : end )1 2 3 4 5 6 7 8a(8)1 2 3 4 5 6 7 8
b(8)

p(1)
p(2)
p(3)
p(4)
!17

shadow/reflect指示文
ステンシルアプリケーションを作成するときに便利な機能
shadow指示文は分散配列に袖領域を追加する
reﬂect指示文は隣接ノードが持つ端の値を自ノードにコピーする

[C]

[F]

#pragma xmp nodes p[3]#pragma xmp template t[9]#pragma xmp distribute t[block] onto p
int a[9];#pragma xmp align a[i] with t[i]#pragma xmp shadow a[1:1]...
#pragma xmp reflect (a)!$xmp nodes p(3)!$xmp template t(9)!$xmp distribute t(block) onto p
integer :: a(9)!$xmp align a(i) with t(i)!$xmp shadow a(1:1)...
!$xmp reflect (a)lower-bound : upper-boundshadow指示文は袖領域（灰色のセル）を追加する!18

shadow/reflect指示文
ステンシルアプリケーションを作成するときに便利な機能
shadow指示文は分散配列に袖領域を追加する
reﬂect指示文は隣接ノードが持つ端の値を自ノードにコピーする

[C]

[F]

#pragma xmp nodes p[3]#pragma xmp template t[9]#pragma xmp distribute t[block] onto p
int a[9];#pragma xmp align a[i] with t[i]#pragma xmp shadow a[1:1]...
#pragma xmp reflect (a)!$xmp nodes p(3)!$xmp template t(9)!$xmp distribute t(block) onto p
integer :: a(9)!$xmp align a(i) with t(i)!$xmp shadow a(1:1)...
!$xmp reflect (a)lower-bound : upper-boundreflect指示文は隣接ノードの端の値を袖領域にコピーする!19shadow/reflect指示文
[C]

[F]

#pragma xmp loop on t[i]for(int i=1;i<9;i++){b[i] = a[i-1] + a[i] + a[i+1];}!$xmp loop on t(i)do i = 2, 8b(i) = a(i-1) + a(i) + a(i+1)
end do!20

shadow/reflect指示文
[C]

[F]

#pragma xmp shadow a[1:1]...
#pragma xmp reflect (a)#pragma xmp loop on t[i]for(int i=1;i<9;i++){b[i] = a[i-1] + a[i] + a[i+1];}!$xmp shadow a(1:1)...
!$xmp reflect (a)!$xmp loop on t(i)do i = 2, 8b(i) = a(i-1) + a(i) + a(i+1)
end do!21

ローカルビュープログラミング
グローバルビュープログラミングは「解くべき問題全体を記述し，それを各ノードが分担する方法を示す」
例：問題1〜100を4人で分担して解け
ローカルビュープログラミングは「各ノードが解くべき問題を個別に示す」（MPIはローカルビュー）
例：ノード1は問題1〜25を解け．ノード2は……
自由度が高い分，グローバルビュープログラミングと比較してやや難しい
ローカルビュープログラミングのための機能として，Fortran 2008から導入したcoarray記法をXMP/Cでもサポート!22

Coarray記法 in XMP/C
Coarrayに対して他ノードからの片側通信（Put/Get）を実行できる
real a(8)real b(8)[*]Fortran2008
b()をcoarrayとして宣言

if(this_image() == 1) thenb(6)[2] = b(4)a(0) = b(3)[2]
end ifimage 1は b(4)をimage 2の b(6)にPutする

sync all

同期（今までに実行した片側通信の終了を待つ）

double a[8];double b[8]:[*];

image 1はimage 2の b(3)を a(0)にGetする

XMP/C

if(xmpc_this_image() == 1){b[6]:[2] = b[4];a[0] = b[3]:[2];}xmpc_sync_all(NULL);!23

部分配列 in XMP/C
XMP/Cにおいて複数の要素に対する片側通信のために，部分配列を導入
Intel Cilk や OpenACCと同等[C] if(xmpc_this_image() == 1){b[10:5]:[2] = b[0:5];
a[:]:[2] = b[:];c[:][9]:[2] = c[:][0];b[0]-b[4]の5要素はimage 2のb[10]-a[14]にPutされる
配列bの全要素はimage 2の配列aにPutされる}

image 2

image 1c[10][10]c[10][10]!24Omni Compileruser code (a.c)$ xmpcc a.c -o a.out(a.out)

XMP指示文を含むユーザコードは，ランタイムの呼び出しに変換する
ランタイムはCとMPIで実装（つまりMPI対応の並列計算環境で動作する）
変換されたコードは，GNU・Intel・PGIなどのネイティブコンパイラで翻訳
オープンソース

https://omni-compiler.org
25

もくじ
XcalableMP（XMP）の概要説明
XMPプログラミング
Lattice QCDの実装!26

Lattice QCD（LQCD）ミニアプリケーション
HPC分野で重要なアプリケーションの1つ
QCD（Quantum Chromo-Dynamics：量子色力学）
は物質の最小単位であるクォークと，クォーク間における相互作用を結ぶグルーオンを表す基本方程式

LQCDは4次元（時間+XYZ軸）の格子上で行うQCDのシミュレーション
LQCDミニアプリケーション
C言語，逐次コード，850行くらい by KEK 松古さん
実アプリケーションBridge++のカーネル部分を抽出

27

Overview of algorithm
Pseudo-code (CG method is used)
S=B// COPY
R=B// COPY
X=B// COPY
sr = norm(S)// NORM
T = WD(U,X)// Main Kernel
S = WD(U,T)// Main Kernel
R=R-S// AXPY
P=R// COPY
rrp = rr = norm(R)// NORM
do{
T = WD(U,P)// Main Kernel
V = WD(U,T)// Main Kernel
pap = dot(V,P)// DOT
cr = rr/pap
X = cr * P + X// AXPY
R = -cr * V + R// AXPY
rr = norm(R)// NORM
bk = rr/rrp
P = bk * P// SCAL
P=P+R// AXPY
rrp = rr}while(rr/sr > 1.E-16)

WD() is the Wilson-Dirac operator

Main kernel (most costly)
Stencil calculation#pragma xmp reflect (X) width(/periodic/..) orthogonal
WD(X, ...);
void WD(Quark_t X[NT][NZ][NY][NX], ... ){:#pragma xmp loop on t[t][z]#pragma omp parallel for collapse(4)for(int t=0;t<NT;t++)for(int z=0;z<NZ;z++)for(int y=0;y<NY;y++)for(int x=0;x<NX;x++){:28

reflect指示文#pragma xmp reflect (a)#pragma xmp reflect (a) width(/periodic/1, ..) ¥orthogonal#pragma xmp reflect (a) orthogonal（orthogonalは実装中）!29

Performance Evaluation on cluster
Oakforest-PACS and COMA
One process per compute node on Oakforest-PACS
Two processes per compute node on COMA because it has two CPU sockets

Problem size is (32,32,32,32) as (NT,NZ,NY,NX) with strong scaling30Performance Evaluation
COMA

Oakforest-PACS

1000

Intel compiler
Omni compiler

1350

750

900

500

450

250

00124816 32 64 128 256

Number of processes

BetterPerformance (GFlops)

1800

1248163264 128 256

Number of processes

Performances of Omni compiler achieve 94 - 105% of those of Intel compiler.

31Productivity Evaluation
Source lines of codes (SLOC)
OpenMP(Base code)

XMP+OpenMP
MPI+OpenMP

1000968750979

Delta SLOC How many lines the code changed
from a base code to a parallel code
180
34% reduce(118/178)
135854
Almost the same

411453Addition← Quantitative
125500452500Qualitative ↓While most of 114 lines
in XMP+OpenMP is theinsertion of XMP
directives,
125 in MPI+OpenMP is acreation of new functions
for communication.

900Modification

XMPMPI++
OpenMP OpenMP

It is easier to develop aparallel application in
XMP+OpenMP than
MPI+OpenMP

32

まとめ
並列言語XMPの紹介
Lattice QCDの実装と評価十分な性能と高い生産性を達成
今回紹介しませんでしたが，色々なベンチマークやミニアプリも実装しています．その内のいくつかは，下記のGitHubで公開中
https://github.com/omni-compiler/XMP-Applications

色々なXMPの拡張
PythonのためのXMPモジュール
アクセラレータ用言語OpenACCとの連携
XMP + OpenACC + α = XcalableACC
アクセラレータを搭載したクラスタシステム用言語
アクセラレータ間の直接通信にも対応
33