View
91
Download
0
Category
Preview:
DESCRIPTION
HPC アプリケーションの OOP を用いた パフォーマンスチューニング. 東京大学 大学院 創造情報学専攻 穂積 俊平 * 伊尾木 将之 千葉 滋. HPC の現状. C 言語や Fortran によって記述されている 実行性能が重要 手続きライブラリが広く利用されている 関数単位での変更のみ可能. HPC の近年の傾向. 実行環境に合わせたチューニングが必要 実行環境のハードウェアが多様化. GPU. コンピュータ クラスタ. マルチ CPU. HPC の近年の傾向. 実行環境に合わせたチューニングが必要 実行環境のハードウェアが多様化. GPU. - PowerPoint PPT Presentation
Citation preview
東京大学 大学院創造情報学専攻
穂積 俊平 * 伊尾木 将之 千葉 滋
HPC アプリケーションのOOP を用いた
パフォーマンスチューニング
2
HPC の現状•C 言語や Fortran によって記述されている- 実行性能が重要
•手続きライブラリが広く利用されている- 関数単位での変更のみ可能
3
HPC の近年の傾向•実行環境に合わせたチューニングが必要- 実行環境のハードウェアが多様化
コンピュータクラスタ
マルチ CPUGPU
4
HPC の近年の傾向•実行環境に合わせたチューニングが必要- 実行環境のハードウェアが多様化
コンピュータクラスタ
マルチ CPUGPU
5
HPC の問題点•粒度の細かいチューニングができない- 手続きライブラリはブラックボックスであり、関数
より粒度の細かい変更が不可能。
入力 出力
メモリ管理
データ転送
6
HPC の問題点•粒度の細かいチューニングができない- 手続きライブラリはブラックボックスであり、関数
より粒度の細かい変更が不可能。
入力 出力
メモリ管理
データ転送
処理 ( 関心事 )ごとに実装を決めたい!
7
例:行列積
A
L
MB
M
N
*
C
L
N
=
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}
①②
③
8
例:行列積
A
L
MB
M
N
*
C
L
N
=
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}
①
j
i
•素直な2重ループ• i と j を入れ替えた2重ループ•GPU で並列実行•MPI で並列実行
9
例:行列積
A
L
MB
M
N
*
C
L
N
=
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}
②
k
k
•素直なループ•展開したループ
10
例:行列積
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}③
A
L
M
M
1次元配列
・・・
•1次元配列•2次元配列•シェアードメモリ•疎行列•対角行列
11
C 言語での実装•保守性のスケーラビリティが得られない- 関数ポインタ- ifdef
12
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
Matmul matmul = new Matmul();
matmul.setTraverse (new Traverse());
matmul.setReduction(new Reduction());
Mat A = new SingleArray(), B = ・・・ , C = ・・・ ;
matmul.calc(A, B, C);
Javaコード
①
②
③
13
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
Matmul matmul = new Matmul();
matmul.setTraverse (new ParallelOnGPU());
matmul.setReduction(new Reduction());
Mat A = new SingleArray(), B = ・・・ , C = ・・・ ;
matmul.calc(A, B, C);
Javaコード
①
②
③
14
オートチューニングへの応用•実装の切り替えが容易
List<Traverse> traverses;
traverses.add(new Traverse());
traverses.add(new ParallelOnGPU());
:
for(Traverse tra : traverses) {
matmul.setTraverse(tra);
matmul.calc(A, B, C);
}
Javaコード
Cのたどり方の実装を変更して実行
15
HPC アプリケーションの特徴•カーネルコードが計算時間の大半を占める
各種設定
カーネルコード
実行時間実行過程
16
WootinJ•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする- ユーザに最適化の条件を満たしているかを提示
実行時コンパイラ
Javaバイトコード
機械語
最適化
コンパイル時チェッカー
Javaソースコード
警告!
17
通常の JIT との違い•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする
Java バイトコード 機械語
最適化
•静的型•動的型
一部をインライン化•オブジェクト•メソッド
18
通常の JIT との違い•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする
Java バイトコード 機械語
最適化
•静的型•動的型•strictfinal
全てをインライン化•オブジェクト•メソッド
19
WootinJ•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする- ユーザに最適化の条件を満たしているかを提示
•strictFinal : 静的に型が一意に決定できる型- プリミティブな型- strictFinal の配列- 自分と親クラスのフィールドが strictFinal であ
り、 final な型
20
WootinJ•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする- ユーザに最適化の条件を満たしているかを提示
Javaソースコード
strictFinal ?
警告!!
21
WootinJ•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする
class Calc { void run(A a){ a.hoge(); }}
static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() );}
最適化
実行
Java コードJava
バイトコード
Java抽象構文木
CUDAコード
機械語
メソッド情報
ユーザが指定したメソッド
22
WootinJ•OOP と実行性能を両立する機構- カーネルコードを強力に JIT コンパイルする
class Calc { void run(A a){ a.hoge(); }}
static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() );}
Java コード
メソッド情報
ユーザが指定したメソッド
メソッドのレシーバ、実引数の型は変換時に一意に決定できる
メソッドのレシーバ、実引数は自由に OOP の抽象化を利用できる
23
実験•目的- WootinJ の実行性能の測定
•実験環境- Tsubame2.0 : 東工大のスパコン
•プログラム- 行列積
24
実験結果
良
• 変換によるオーバーヘッドの分だけ WootinJ の方が遅かったが、 OOP の抽象化による差は見られなかった。
25
関連研究•Firepile [Nathaniel Nystrom ら・ 2011]- Scala から OpenCL への実行時変換器。動的束縛は
Switch 文を用いて表現
•Aparapi- Java から OpenCL への実行時変換器。オブジェクト
の利用はできない
26
まとめと今後の課題•まとめ- オブジェクト指向を利用する事で、実行環境に合わ
せたパフォーマンスチューニングができる事を述べた。
- オブジェクト指向と実行性能を両立する機構としてWootinJ を開発した。
•今後の課題- WootinJ を利用したフレームワークの作成- C++ との比較
36
C の要素のたどり方for (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k]*B[k * N +
j];
}
}
}
①
GPU を利用した並列実行dim3 grid(N/BS, L/BS),
block(BS,BS);
traverse<<<grid, block>>>(A, B, C);
_global_ void traverse(float *
A ・・ ){
int i = BS*blockIdx.y +
threadIdx.y;
int j = BS*blockIdx.x +
threadIdx.x;
:
}
i と j を入れ替えた2重ループ
for (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
:
}
}
素直な2重ループ
for (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
:
}
}
37
C の1要素の求め方
for (int k = 0; k < M; k++) {
C[i*N + j] += A[i*M + k]*B[k*N +
j];
}
素直なループ
for (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k]*B[k * N +
j];
}
}
}
②
for (int k = 0; k < M; k+=2) {
C[i*N + j] += A[i*M + k] * B[k*N + j];
C[i*N + j] += A[i*M + k + 1] * B[(k+1)*N +
j];
}
展開したループ
38
行列の表現方法•言語上の確保の仕方の違い- 1次元配列- 2次元配列
•ハードウェア的な違い- シェアードメモリの利用
•行列の種類による違い- 疎行列- 対角行列
39
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
Matmul matmul = new Matmul();
matmul.iterate = new LoopOnCPU();
matmul.reduction = new Reduction();
Matrix A = new SingleArray(),
B = ・・・ , C = ・・・ ;
matmul.calc(A, B, C);
Javaコード
①
②
③
40
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
Matmul matmul = new Matmul();
matmul.iterate = new LoopOnCPU();
matmul.reduction = new
UnrollingReduction();
Matrix A = new SingleArray(),
B = ・・・ , C = ・・・ ;
matmul.calc(A, B, C);
Javaコード
①
②
③
41
実行環境に合わせたチューニング
•ユーザによる設定ができる形で機能提供すべき- C 言語や Fortran では難しい
•手続きライブラリはブラックボックス- ブラックボックス内の実装の変更が困難
入力 出力
複数の処理( 関心事 ) が含まれる
メモリ管理
データ転送
42
•複数の関心事に分割できる
行列積に含まれる関心事
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}A
L
MB
M
N
*
C
L
N
=
①
j
i
43
•複数の関心事に分割できる
行列積に含まれる関心事
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}A
L
MB
M
N
*
C
L
N
=
②
k
k
44
•複数の関心事に分割できる
行列積に含まれる関心事
C コードfor (int i = 0; i < L; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N +
j];
}}}A
L
M
③
M
1次元配列
・・・
45
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
Matmul matmul = new Matmul();
matmul.setTraverse(Traverse.factory(“simpleLoop”
));
matmul.setReduction(Reduction.factory(“unrolling
”));
Matrix A = new SingleArray(), B = ・・・ , C
= ・・・ ;
matmul.calc(A, B, C);
Javaコード
①
②
③
46
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
void calc(Mat A, Mat B, Mat C) { tra.calc(A, B, C, red);}
Matmulvoid calc(Mat A, Mat B, Mat C, Reduction red) { for(int i = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getCols(); j++) { red.calc(A, B, C);}}}
Traverse
void calc(Mat A, Mat B, Mat C){ for(int k = 0; k < A.getCols(); k++) { C[i*C.getCols()+j] += A[i*A.getCols()+k] * B[k*B.getCols()+j];}}
Reduction
void calc(Mat A, Mat B, Mat C, Reduction red) {for(int j = 0; j < C.getCols(); j++) { for(int i = 0; i < C.getRows(); i++) { red.calc(A, B, C);}}}
ReverseTraverse
47
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
void calc(Mat A, Mat B, Mat C) { tra.calc(A, B, C, red);}
Matmul
void calc(Mat A, Mat B, Mat C, Reduction red) { for(int i = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getCols(); j++) { red.calc(A, B, C);}}}
Traverse
void calc(Mat A, Mat B, Mat C, Reduction red) {for(int j = 0; j < C.getCols(); j++) { for(int i = 0; i < C.getRows(); i++) { red.calc(A, B, C);}}}
ReverseTraverse
48
近年の傾向•実行環境に合わせたチューニングが必要- ハードウェアが複雑化している• GPU
• コンピュータ・クラスタ• マルチコア CPU
GPU
49
関数による分離
C コードvoid matmul(float *A, float *B, float *C) {
traverse(A, B, C);
}
void traverse(float *A, float *B, float *C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
reduction(A, B, C, i, j);
}}}
void reduction(float *A, float *B, float *C, int i, int
j) {
for(int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N + j];
}}
50
手続きライブラリの限界•粒度の細かいチューニングができない- 手続きライブラリはブラックボックスであり、関数
より粒度の細かい変更が不可能。
入力 出力
メモリ管理
データ転送
他の実装へ変更
51
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
class Matmul { Traverse traverse; Reduction reduction;
void calc(Matrix A, Matrix B, Matrix C) { traverse.calc(A, B, C, reduction); }}
Javaコード
52
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
class SimpleDoubleLoop implements Traverse { void calc(Matrix A, Matrix B, Matrix C, Reduction red) { for(int i = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getColumns(); j++) { reduction.calc(A, B, C); } } }}
Javaコード
53
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
class SimpleLoop implements Reduction { void calc(Matrix A, Matrix B, Matrix C){ for(int k = 0; k < A.getColumns(); k++) { C[i*C.getColumns()+j] += A[i*A.getColumns()+k] * B[k*B.getColumns()+j]; } }}
Javaコード
54
オブジェクト指向を用いたチューニング
•ユーザによる設定が可能な形で機能提供ができる
void calc(Matrix A, Matrix B, Matrix C) { traverse.calc(A, B, C, reduction);}
Matmul
void calc(Matrix A, Matrix B, Matrix C, Reduction red) { for(int i = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getColumns(); j++) { reduction.calc(A, B, C);}}}
Traverse
void calc(Matrix A, Matrix B, Matrix C){ for(int k = 0; k < A.getColumns(); k++) { C[i*C.getColumns()+j] += A[i*A.getColumns()+k] * B[k*B.getColumns()+j];}}
Reduction
55
関数ポインタによる変更C コード
int (*traP)(float *A, float *B, float *C) = &traverse;
int (*redP)(float *A, float *B, float *C, int i, int j) = &reduction;
void matmul(float *A, float *B, float *C) {
(traP)(A, B, C);
}
void traverse(float *A, float *B, float *C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
(redP)(A, B, C, i, j);
}}}
void reduction(float *A, float *B, float *C, int i, int j) {
for(int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N + j];
}}
void traverse(float *A, float *B, float *C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
(redP)(A, B, C, i, j);
}}}
void reverseTraverse(float *A, float *B, float
*C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
(redP)(A, B, C, i, j);
}}}
56
関数による分離
C コードvoid matmul(float *A, float *B, float *C) {
traverse(A, B, C);
}
void traverse(float *A, float *B, float *C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
reduction(A, B, C, i, j);
}}}
void reduction(float *A, float *B, float *C, int i, int
j) {
for(int k = 0; k < M; k++) {
C[i * N + j] += A[i * M + k] * B[k * N + j];
}}
57
手続きライブラリの利用•実行環境に合ったライブラリを利用する事で、アプリケーションのチューニングができる。
• GotoBLAS
• cublas
• ・・・・
線形代数ライブラリCPU
GPU
58
関数ポインタによる変更int (*traP)(float *A, float *B, float *C);
int (*redP)(float *A, float *B, float *C, int i, int j);
void matmul(float *A, float *B, float *C) {
(traP)(A, B, C);
}
void traverse(float *A, float *B, float *C) {
for(int i = 0; i < L; i++) {
for(int j = 0; j < N; j++) {
(redP)(A, B, C, i, j);
}}}
void reverseTraverse(float *A, float *B, float
*C) {
for(int j = 0; j < N; j++) {
for(int i = 0; i < L; i++) {
(redP)(A, B, C, i, j);
}}}
59
C 言語による実装の限界•関数ポインタ- 安全でない
•フラグによる制御- ライブラリ作成者がすべての状況を想定できない
60
オブジェクト指向を用いたチューニング
•安全に実装の変更が可能
void calc(Mat A, Mat B, Mat C) { tra.calc(A, B, C, red);}
Matmul
void calc(Mat A, Mat B, Mat C, Reduction red) { for(int i = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getCols(); j++) { red.calc(A, B, C);}}}
Traverse
void calc(Mat A, Mat B, Mat C, Reduction red) {for(int j = 0; j < C.getCols(); j++) { for(int i = 0; i < C.getRows(); i++) { red.calc(A, B, C);}}}
ReverseTraverse
61
モジュラリティと実行性能•WootinJ が生成するコードには Java の抽象化が含まれていない
Java
• 動的束縛
• オブジェクト
どうする??
CUDA
• switch 文?
• 構造体?
Java のサブセットを提供
62
• 動的メソッド呼び出しの除去
実行時情報を利用した最適化
A
+hoge()
class Calc { void run(A a){ a.hoge(); }}
static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new B() );}
Java コード
実行時情報 void run(A a) { B_hoge();}
void B_hoge() { :}
静的呼び出しに変換
B
+hoge()
63
• オブジェクトの除去
実行時情報を利用した最適化
A
int x;int y;
class Calc { void run(A a){ int sum = a.x + a.y }}
static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() );}
Java コード
実行時情報
void run(int a_x, int a_y) { int sum = a_x + a_y;}
プリミティブな値の利用に変換
64
•Java バイトコードから CUDA C への実行時変換器- 実行時情報を利用し、抽象化のオーバーヘッドを除去
WootinJ
class Calc { void run(A a){ a.hoge(); }}
static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() );} 最適化
実行
Java コードJava
バイトコード
Java抽象構文木
CUDAコード
機械語
メソッド情報
65
WootinJ•OOP と実行性能を両立する機構- カーネルコードをアグレッシブに JIT コンパイルする- コンパイル時に条件 (strictFinal) を満たしているか確認
OOP の抽象化が含まれないコード
以下が含まれない•動的束縛•オブジェクト
66
実験結果• 変換によるオーバーヘッドの分だけ WootinJ の方が
遅かったが、 OOP の抽象化による差は見られなかった。
良
Recommended