1 / 57

HPC アプリケーションの OOP を用いた パフォーマンスチューニング

HPC アプリケーションの OOP を用いた パフォーマンスチューニング. 東京大学 大学院 創造情報学専攻 穂積 俊平 *  伊尾木 将之 千葉 滋. HPC の現状. C 言語や Fortran によって記述されている 実行性能が重要 手続きライブラリが広く利用されている 関数単位での変更のみ可能. HPC の近年の傾向. 実行環境に合わせたチューニングが必要 実行環境のハードウェアが多様化. GPU. コンピュータ クラスタ. マルチ CPU. HPC の近年の傾向. 実行環境に合わせたチューニングが必要 実行環境のハードウェアが多様化. GPU.

avel
Download Presentation

HPC アプリケーションの OOP を用いた パフォーマンスチューニング

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. HPCアプリケーションのOOPを用いたパフォーマンスチューニングHPCアプリケーションのOOPを用いたパフォーマンスチューニング 東京大学大学院 創造情報学専攻 穂積俊平* 伊尾木将之 千葉滋

  2. HPCの現状 • C言語やFortranによって記述されている • 実行性能が重要 • 手続きライブラリが広く利用されている • 関数単位での変更のみ可能

  3. HPCの近年の傾向 • 実行環境に合わせたチューニングが必要 • 実行環境のハードウェアが多様化 GPU コンピュータ クラスタ マルチCPU

  4. HPCの近年の傾向 • 実行環境に合わせたチューニングが必要 • 実行環境のハードウェアが多様化 GPU コンピュータ クラスタ マルチCPU

  5. HPCの問題点 • 粒度の細かいチューニングができない • 手続きライブラリはブラックボックスであり、関数より粒度の細かい変更が不可能。 入力 出力 メモリ管理 データ転送

  6. HPCの問題点 • 粒度の細かいチューニングができない • 手続きライブラリはブラックボックスであり、関数より粒度の細かい変更が不可能。 入力 出力 処理(関心事)ごとに実装を 決めたい! メモリ管理 データ転送

  7. 例:行列積 C B A M N N M L L ① ② Cコード for (inti = 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. 例:行列積 C B A M N N • 素直な2重ループ • iと j を入れ替えた2重ループ • GPUで並列実行 • MPIで並列実行 M L L ① Cコード for (inti = 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

  9. 例:行列積 C B A M N N • 素直なループ • 展開したループ M L L ② Cコード for (inti = 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. 例:行列積 A M • 1次元配列 • 2次元配列 • シェアードメモリ • 疎行列 • 対角行列 L Cコード for (inti = 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]; }}} ③ 1次元配列 M ・・・

  11. C言語での実装 • 保守性のスケーラビリティが得られない • 関数ポインタ • ifdef

  12. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード Matmulmatmul = new Matmul(); matmul.setTraverse (new Traverse()); matmul.setReduction(new Reduction()); Mat A = new SingleArray(), B =・・・, C =・・・; matmul.calc(A, B, C); ① ② ③

  13. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード Matmulmatmul = new Matmul(); matmul.setTraverse (new ParallelOnGPU()); matmul.setReduction(new Reduction()); Mat A = new SingleArray(), B =・・・, C =・・・; matmul.calc(A, B, C); ① ② ③

  14. オートチューニングへの応用 • 実装の切り替えが容易 Javaコード List<Traverse> traverses; traverses.add(new Traverse()); traverses.add(new ParallelOnGPU()); : for(Traverse tra: traverses) { matmul.setTraverse(tra); matmul.calc(A, B, C); } Cのたどり方の実装を 変更して実行

  15. HPCアプリケーションの特徴 • カーネルコードが計算時間の大半を占める 実行過程 実行時間 各種設定 カーネル コード

  16. WootinJ • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする • ユーザに最適化の条件を満たしているかを提示 実行時 コンパイラ コンパイル時 チェッカー Java ソースコード Java バイトコード 最適化 機械語 警告!

  17. 通常のJITとの違い • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする 一部をインライン化 • オブジェクト • メソッド • 静的型 • 動的型 最適化 Java バイトコード 機械語

  18. 通常のJITとの違い • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする • 静的型 • 動的型 • strictfinal 全てをインライン化 • オブジェクト • メソッド 最適化 Java バイトコード 機械語

  19. WootinJ • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする • ユーザに最適化の条件を満たしているかを提示 • strictFinal :静的に型が一意に決定できる型 • プリミティブな型 • strictFinalの配列 • 自分と親クラスのフィールドがstrictFinalであり、finalな型

  20. WootinJ • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする • ユーザに最適化の条件を満たしているかを提示 strictFinal ? Java ソースコード 警告!!

  21. WootinJ • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする メソッド情報 ユーザが指定したメソッド Javaコード Java バイトコード static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() ); } Java 抽象構文木 最適化 class Calc { void run(A a){ a.hoge(); } } CUDA コード 実行 機械語

  22. WootinJ • OOPと実行性能を両立する機構 • カーネルコードを強力にJITコンパイルする メソッド情報 ユーザが指定したメソッド Javaコード メソッドのレシーバ、実引数の 型は変換時に一意に決定できる メソッドのレシーバ、実引数は 自由にOOPの抽象化を利用できる static void main(String[] args) { CUDARunner.invoke( new Calc(), “run”, new A() ); } class Calc { void run(A a){ a.hoge(); } }

  23. 実験 • 目的 • WootinJの実行性能の測定 • 実験環境 • Tsubame2.0 : 東工大のスパコン • プログラム • 行列積

  24. 実験結果 • 変換によるオーバーヘッドの分だけWootinJの方が遅かったが、OOPの抽象化による差は見られなかった。 良

  25. 関連研究 • Firepile[Nathaniel Nystromら・2011] • ScalaからOpenCLへの実行時変換器。動的束縛はSwitch文を用いて表現 • Aparapi • JavaからOpenCLへの実行時変換器。オブジェクトの利用はできない

  26. まとめと今後の課題 • まとめ • オブジェクト指向を利用する事で、実行環境に合わせたパフォーマンスチューニングができる事を述べた。 • オブジェクト指向と実行性能を両立する機構としてWootinJを開発した。 • 今後の課題 • WootinJを利用したフレームワークの作成 • C++との比較

  27. Cの要素のたどり方 for (inti = 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を利用した並列実行 iとjを入れ替えた2重ループ 素直な2重ループ dim3 grid(N/BS, L/BS), block(BS,BS); traverse<<<grid, block>>>(A, B, C); _global_ void traverse(float *A・・){ inti = BS*blockIdx.y + threadIdx.y; int j = BS*blockIdx.x + threadIdx.x;          : } for (inti = 0; i < L; i++) { for (int j = 0; j < N; j++) {         : } } for (inti = 0; i < L; i++) { for (int j = 0; j < N; j++) {         : } }

  28. Cの1要素の求め方 素直なループ 展開したループ 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]; } for (inti = 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]; } } } ②

  29. 行列の表現方法 • 言語上の確保の仕方の違い • 1次元配列 • 2次元配列 • ハードウェア的な違い • シェアードメモリの利用 • 行列の種類による違い • 疎行列 • 対角行列

  30. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード Matmulmatmul = new Matmul(); matmul.iterate = new LoopOnCPU(); matmul.reduction= new Reduction(); Matrix A = new SingleArray(), B =・・・, C =・・・; matmul.calc(A, B, C); ① ② ③

  31. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード Matmulmatmul = new Matmul(); matmul.iterate = new LoopOnCPU(); matmul.reduction= new UnrollingReduction(); Matrix A = new SingleArray(), B =・・・, C =・・・; matmul.calc(A, B, C); ① ② ③

  32. 実行環境に合わせたチューニング • ユーザによる設定ができる形で機能提供すべき • C言語やFortranでは難しい • 手続きライブラリはブラックボックス • ブラックボックス内の実装の変更が困難 入力 出力 複数の処理 (関心事)が 含まれる メモリ管理 データ転送

  33. 行列積に含まれる関心事 C B A M N N • 複数の関心事に分割できる M L L ① Cコード for (inti = 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

  34. 行列積に含まれる関心事 C B A M N N • 複数の関心事に分割できる M L L ② Cコード for (inti = 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

  35. 行列積に含まれる関心事 A M • 複数の関心事に分割できる L Cコード for (inti = 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]; }}} ③ 1次元配列 M ・・・

  36. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード Matmulmatmul = new Matmul(); matmul.setTraverse(Traverse.factory(“simpleLoop”)); matmul.setReduction(Reduction.factory(“unrolling”)); Matrix A = new SingleArray(), B =・・・, C =・・・; matmul.calc(A, B, C); ① ② ③

  37. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Matmul void calc(Mat A, Mat B, Mat C) { tra.calc(A, B, C, red); } Traverse ReverseTraverse void calc(Mat A, Mat B, Mat C, Reduction red) { for(inti = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getCols(); j++) { red.calc(A, B, C); }}} void calc(Mat A, Mat B, Mat C, Reduction red) { for(int j = 0; j < C.getCols(); j++) { for(inti = 0; i < C.getRows(); i++) { red.calc(A, B, C); }}} Reduction 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]; }}

  38. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Matmul void calc(Mat A, Mat B, Mat C) { tra.calc(A, B, C, red); } Traverse ReverseTraverse void calc(Mat A, Mat B, Mat C, Reduction red) { for(inti = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getCols(); j++) { red.calc(A, B, C); }}} void calc(Mat A, Mat B, Mat C, Reduction red) { for(int j = 0; j < C.getCols(); j++) { for(inti = 0; i < C.getRows(); i++) { red.calc(A, B, C); }}}

  39. 近年の傾向 • 実行環境に合わせたチューニングが必要 • ハードウェアが複雑化している • GPU • コンピュータ・クラスタ • マルチコアCPU GPU

  40. 関数による分離 Cコード void matmul(float *A, float *B, float *C) { traverse(A, B, C); } void traverse(float *A, float *B, float *C) { for(inti = 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, inti, int j) { for(int k = 0; k < M; k++) { C[i * N + j] += A[i * M + k] * B[k * N + j]; }}

  41. 手続きライブラリの限界 • 粒度の細かいチューニングができない • 手続きライブラリはブラックボックスであり、関数より粒度の細かい変更が不可能。 他の実装へ 変更 入力 出力 メモリ管理 データ転送

  42. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード class Matmul { Traverse traverse; Reduction reduction; void calc(Matrix A, Matrix B, Matrix C) { traverse.calc(A, B, C, reduction); } }

  43. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード class SimpleDoubleLoop implements Traverse { void calc(Matrix A, Matrix B, Matrix C, Reduction red) { for(inti = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getColumns(); j++) { reduction.calc(A, B, C); } } } }

  44. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Javaコード 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]; } } }

  45. オブジェクト指向を用いたチューニング • ユーザによる設定が可能な形で機能提供ができる Matmul Traverse Reduction void calc(Matrix A, Matrix B, Matrix C) { traverse.calc(A, B, C, reduction); } void calc(Matrix A, Matrix B, Matrix C, Reduction red) { for(inti = 0; i < C.getRows(); i++) { for(int j = 0; j < C.getColumns(); j++) { reduction.calc(A, B, C); }}} 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]; }}

  46. 関数ポインタによる変更 void traverse(float *A, float *B, float *C) { for(inti = 0; i < L; i++) { for(int j = 0; j < N; j++) { (redP)(A, B, C, i, j); }}} Cコード int (*traP)(float *A, float *B, float *C) = &traverse; int (*redP)(float *A, float *B, float *C, inti, int j) = &reduction; void matmul(float *A, float *B, float *C) { (traP)(A, B, C); } void traverse(float *A, float *B, float *C) { for(inti = 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, inti, int j) { for(int k = 0; k < M; k++) { C[i * N + j] += A[i * M + k] * B[k * N + j]; }} void reverseTraverse(float *A, float *B, float *C) { for(inti = 0; i < L; i++) { for(int j = 0; j < N; j++) { (redP)(A, B, C, i, j); }}}

  47. 関数による分離 Cコード void matmul(float *A, float *B, float *C) { traverse(A, B, C); } void traverse(float *A, float *B, float *C) { for(inti = 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, inti, int j) { for(int k = 0; k < M; k++) { C[i * N + j] += A[i * M + k] * B[k * N + j]; }}

  48. 手続きライブラリの利用 • 実行環境に合ったライブラリを利用する事で、アプリケーションのチューニングができる。 線形代数ライブラリ • GotoBLAS • cublas • ・・・・ CPU GPU

  49. 関数ポインタによる変更 int(*traP)(float *A, float *B, float *C); int (*redP)(float *A, float *B, float *C, inti, int j); void matmul(float *A, float *B, float *C) { (traP)(A, B, C); } void reverseTraverse(float *A, float *B, float *C) { for(int j = 0; j < N; j++) { for(inti = 0; i < L; i++) { (redP)(A, B, C, i, j); }}} void traverse(float *A, float *B, float *C) { for(inti = 0; i < L; i++) { for(int j = 0; j < N; j++) { (redP)(A, B, C, i, j); }}}

  50. C言語による実装の限界 • 関数ポインタ • 安全でない • フラグによる制御 • ライブラリ作成者がすべての状況を想定できない

More Related