OpenACC入門

Similar documents
概要 OpenACC とは OpenACC について OpenMP, CUDA との違い OpenACC の指示文 並列化領域指定指示文 (kernels/parallel) データ移動指示文 ループ指示文 OpenACC の実用例 実習 コンパイラメッセージの見方 OpenACC プログラムの実装

第12回講義(2019年7月17日)

GPU チュートリアル :OpenACC 篇 Himeno benchmark を例題として 高エネルギー加速器研究機構 (KEK) 松古栄夫 (Hideo Matsufuru) 1 December 2018 HPC-Phys 理化学研究所 共通コードプロジェクト

OpenACCによる並列化

CUDA 連携とライブラリの活用 2

1. GPU コンピューティング GPU コンピューティング GPUによる 汎用コンピューティング GPU = Graphics Processing Unit CUDA Compute Unified Device Architecture NVIDIA の GPU コンピューティング環境 Lin

Slides: TimeGraph: GPU Scheduling for Real-Time Multi-Tasking Environments

コードのチューニング

熊本大学学術リポジトリ Kumamoto University Repositor Title GPGPU による高速演算について Author(s) 榎本, 昌一 Citation Issue date Type URL Presentation

01_OpenMP_osx.indd

TSUBAME2.0 における GPU の 活用方法 東京工業大学学術国際情報センター丸山直也第 10 回 GPU コンピューティング講習会 2011 年 9 月 28 日

演習1: 演習準備

研究背景 大規模な演算を行うためには 分散メモリ型システムの利用が必須 Message Passing Interface MPI 並列プログラムの大半はMPIを利用 様々な実装 OpenMPI, MPICH, MVAPICH, MPI.NET プログラミングコストが高いため 生産性が悪い 新しい並

memo

Vol.214-HPC-145 No /7/3 C #pragma acc directive-name [clause [[,] clause] ] new-line structured block Fortran!$acc directive-name [clause [[,] c

CUDA を用いた画像処理 画像処理を CUDA で並列化 基本的な並列化の考え方 目標 : 妥当な Naïve コードが書ける 最適化の初歩がわかる ブロックサイズ メモリアクセスパターン

GPGPUクラスタの性能評価

Slide 1

GPU GPU CPU CPU CPU GPU GPU N N CPU ( ) 1 GPU CPU GPU 2D 3D CPU GPU GPU GPGPU GPGPU 2 nvidia GPU CUDA 3 GPU 3.1 GPU Core 1

3次多項式パラメタ推定計算の CUDAを用いた実装 (CUDAプログラミングの練習として) Implementation of the Estimation of the parameters of 3rd-order-Polynomial with CUDA

(Microsoft PowerPoint \215u\213`4\201i\221\272\210\344\201j.pptx)

07-二村幸孝・出口大輔.indd

GPU n Graphics Processing Unit CG CAD

修士論文

PowerPoint プレゼンテーション

openmp1_Yaguchi_version_170530

OpenACC

OpenMPプログラミング

TopSE並行システム はじめに

Microsoft Word ●IntelクアッドコアCPUでのベンチマーク_吉岡_ _更新__ doc

04-process_thread_2.ppt

スライド 1

Microsoft PowerPoint - 09.pptx

GPUコンピューティング講習会パート1

DO 時間積分 START 反変速度の計算 contravariant_velocity 移流項の計算 advection_adams_bashforth_2nd DO implicit loop( 陰解法 ) 速度勾配, 温度勾配の計算 gradient_cell_center_surface 速

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

CCS HPCサマーセミナー 並列数値計算アルゴリズム

ストリームを用いたコンカレントカーネルプログラミングと最適化 エヌビディアジャパン CUDAエンジニア森野慎也 GTC Japan 2014

(速報) Xeon E 系モデル 新プロセッサ性能について

最新の並列計算事情とCAE

PowerPoint Presentation

Microsoft PowerPoint - 演習1:並列化と評価.pptx

GPUを用いたN体計算

メソッドのまとめ

RX ファミリ用 C/C++ コンパイラ V.1.00 Release 02 ご使用上のお願い RX ファミリ用 C/C++ コンパイラの使用上の注意事項 4 件を連絡します #pragma option 使用時の 1 または 2 バイトの整数型の関数戻り値に関する注意事項 (RXC#012) 共用

Microsoft PowerPoint - CproNt02.ppt [互換モード]

( CUDA CUDA CUDA CUDA ( NVIDIA CUDA I

Images per Second Images per Second VOLTA: ディープラーニングにおける大きな飛躍 ResNet-50 トレーニング 2.4x faster ResNet-50 推論 TensorRT - 7ms レイテンシ 3.7x faster P100 V100 P10

2012年度HPCサマーセミナー_多田野.pptx

第9回 配列(array)型の変数

Microsoft PowerPoint - sales2.ppt

GPU のアーキテクチャとプログラム構造 長岡技術科学大学電気電子情報工学専攻出川智啓

cp-7. 配列

Microsoft PowerPoint - GPGPU実践基礎工学(web).pptx

Microsoft PowerPoint - CAEworkshop_ _01.ver1.3

コンピュータ工学講義プリント (7 月 17 日 ) 今回の講義では フローチャートについて学ぶ フローチャートとはフローチャートは コンピュータプログラムの処理の流れを視覚的に表し 処理の全体像を把握しやすくするために書く図である 日本語では流れ図という 図 1 は ユーザーに 0 以上の整数 n

Transcription:

第 87 回 OpenMP/OpenACC による マルチコア メニィコア並列プログラミング 入門 星野哲也 (hoshino@cc.u-tokyo.ac.jp) 東京大学情報基盤センター 2017/11/1 ( 水 )

スケジュール (11 月 1 日 ) 13:00 14:30 GPUについて GPUのアーキテクチャ GPUプログラミングで気をつけるべきこと OpenACC OpenMPとOpenACCの違い OpenACCの指示文 アプリケーションのOpenACC 化 14:45-17:30 ( 適宜休憩 ) ずっと実習 コンパイラメッセージの見方 実行の仕方 ICCGソルバーのOpenACC 化 マルチコア メニィコア並列プログラミング入門 2

GPU について GPU のアーキテクチャ GPU プログラミングで気をつけるべきこと 3

Reedbush-H ノードのブロック図 128GB DDR4 DDR4 DDR4 DDR4 76.8GB/s Intel Xeon E5-2695 v4 (Broadwell- EP) G3x 16 76.8GB/s QPI QPI 76.8GB/s 15.7 GB/s 15.7 GB/s Intel Xeon E5-2695 v4 (Broadwell-EP) G3 x16 DDR4 DDR4 DDR4 DDR4 76.8GB/s 128GB PCIe sw PCIe sw IB FDR HCA G3 x16 NVIDIA Pascal 20 GB/s NVLinK NVLinK 20 GB/s G3 x16 NVIDIA Pascal IB FDR HCA EDR switch EDR マルチコア メニィコア並列プログラミング入門 4

GPU って何? GPU : Graphics Processing Unit いわゆるグラフィックボード ( グラボ ) ビデオカード 画像処理に特化したハードウェア 高速 高解像度描画 3D 描画処理 ( 透視変換 陰影 照明 ) 画面出力 ノート PC ゲームハード スマホなどに搭載 GPU を汎用計算に用いる手法を GPGPU GPU コンピューティングなどと呼ぶ マルチコア メニィコア並列プログラミング入門 5

なぜ GPU コンピューティング? 性能が高いから! P100 BDW KNL 動作周波数 (GHz) 1.480 2.10 1.40 コア数 ( 有効スレッド数 ) 3,584 18 (18) 68 (272) 理論演算性能 (GFLOPS) 5,304 604.8 3,046.4 主記憶容量 (GB) 16 128 16 メモリバンド幅 (GB/sec., Stream Triad) 備考 534 65.5 490 Reedbush-H の GPU Reedbush-U/H の CPU Oakforest-PACS の CPU (Intel Xeon Phi) マルチコア メニィコア並列プログラミング入門 6

GPU プログラミングは何が難しい? CPU: 大きなコアをいくつか搭載 Reedbush-H の CPU : 2.10 GHz 18 コア 大きなコア 分岐予測 パイプライン処理 Out-of-Order 要はなんでもできる 逐次の処理が得意 GPU: 小さなコアをたくさん搭載 Reedbush-H の GPU: 1.48 GHz 3,584 コア 小さなコア... 上記機能が弱いまたはない! 並列処理が必須 GPUの難しさ 1. 多数のコアを効率良く扱う難しさ 2. 並列プログラミング自体の難しさ マルチコア メニィコア並列プログラミング入門 7

参考 :NVIDIA Tesla P100 56 SMs 3584 CUDA Cores 16 GB HBM2 P100 whitepaper より マルチコア メニィコア並列プログラミング入門 8

参考 :NVIDIA Tesla P100 の SM マルチコア メニィコア並列プログラミング入門 9

参考 :NVIDIA 社の GPU 製品シリーズ GeForce コンシューマ向け 安価 Tesla HPC 向け 倍精度演算器 大容量メモリ ECC を備えるため高価 アーキテクチャ ( 世代 ) 1. Tesla: 最初の HPC 向け GPU TSUBAME1.2 など 2. Fermi:2 世代目 TSUBAME2.0 など ECC メモリ FMA 演算 L1 L2 キャッシュ 3. Kepler: 現在 HPC にて多く利用 TSUBAME2.5 など シャッフル命令 Dynamic Parallelism Hyper-Q 4. Maxwell: コンシューマ向けのみ 5. Pascal: 最新 GPU Reedbush-H に搭載 HBM2 半精度演算 NVLink 倍精度 atomicadd など 6. Volta: 次世代 GPU Tensor Core など マルチコア メニィコア並列プログラミング入門 10

押さえておくべき GPU の特徴 CPUと独立のGPUメモリ 性能を出すためにはスレッド数 >> コア数 階層的スレッド管理と同期 Warp 単位の実行 やってはいけないWarp 内分岐 コアレスドアクセス マルチコア メニィコア並列プログラミング入門 11

CPU と独立の GPU メモリ 1. 必要なデータを送る ノードの外へ バス (PCIe など ) ~20GB/s CPU OS が動いている ~32GB/s GPU OS は存在しない 3. 計算結果を返す 2. 計算を行う ~200GB/s ~1,000GB/s メインメモリ デバイスメモリ 計算は CPU から始まる 物理的に独立のデバイスメモリとデータのやり取り必須 マルチコア メニィコア並列プログラミング入門 12

性能を出すためにはスレッド数 >> コア数 推奨スレッド数 CPU: スレッド数 = コア数 ( 高々数十スレッド ) GPU: スレッド数 >= コア数 *4~ ( 数万 ~ 数百万スレッド ) 最適値は他のリソースとの兼ね合いによる 理由 : 高速コンテキストスイッチによるメモリレイテンシ隠し CPU : レジスタ スタックの退避は OS がソフトウェアで行う ( 遅い ) GPU : ハードウェアサポートでコストほぼゼロ メモリアクセスによる暇な時間 ( ストール ) に他のスレッドを実行 メモリ read 開始 メモリ read 終了 1core=1 スレッドのとき 1core=N スレッドのとき マルチコア メニィコア並列プログラミング入門 13

階層的スレッド管理と同期 スレッドブロック 階層的なコア / スレッド管理 P100 は 56 SM を持ち 1 SM は 64 CUDA core を持つ トータル 3584 CUDA core 1 SM が複数のスレッドブロックを担当し 1 CUDA core が複数スレッドを担当 スレッド間の同期 同一スレッドブロック内のスレッドは同期できる 異なるスレッドブロックに属するスレッド間は同期できない 同期するためには GPU の処理を終了する必要あり atomic 演算は可能 cited from : http://cudaprogramming.blogspot.jp/2012/12/thread-hierarchy-in-cudaprogramming.html マルチコア メニィコア並列プログラミング入門 14

Warp 単位の実行 連続した32スレッドを1 単位 = Warp と呼ぶ このWarpは足並み揃えて動く 実行する命令は32スレッド全て同じ データは違ってもいい スレッド 1 2 3 31 32 配列 A 4 3 5 8 0 配列 B 2 3 1 1 9 OK! スレッド 1 2 3 31 32 配列 A 4 3 5 8 0 + 配列 B 2 3 1 1 9 NG! マルチコア メニィコア並列プログラミング入門 15

やってはいけない Warp 内分岐 Divergent Branch Warp 内で分岐すること Warp 単位の分岐なら OK : : if ( TRUE ) { : : else { : : : : : : if ( 奇数スレッド ) { : : else { : : : : else 部分は実行せずジャンプ マルチコア メニィコア並列プログラミング入門 一部スレッドを眠らせて全分岐を実行最悪ケースでは32 倍のコスト 16

コアレスドアクセス 同じ Warp 内のスレッドが近いアドレスに同時にアクセスするのがメモリの性質上効率的 これをコアレスドアクセス (coalesced access) と呼ぶ スレッド 1 2 3 4 32 デバイスメモリ メモリアクセスが 1 回で済む スレッド 1 2 3 4 32 32 回のメモリアクセスが行われる 128バイト単位でメモリアクセス Warp 内のアクセスが128バイトに収まってれば1 回 外れればその分だけ繰り返す 最悪ケースでは32 倍のコスト マルチコア メニィコア並列プログラミング入門 17

ストライドアクセスがあるとどうなるか Memory Bandwidth [GB/sec] GPU はストライドアクセスに弱い! 600 500 400 300 200 100 void AoS_STREAM_Triad(STREAM_TYPE scalar) { ssize_t i,j; #pragma omp parallel for private(i,j) #pragma acc kernels present(a_aos[0:stream_array_size],b_aos[0:stream_array_size],c_aos[0:stream_array_size]) #pragma acc loop gang vector independent for (j=0; j<stream_array_size/stride; j++) for (i=0; i<stride; i++) a_aos[j*stride+i] = b_aos[j*stride+i]+scalar*c_aos[j*stride+i]; ストライドアクセス付き stream triad 0 0 20 40 60 80 100 120 140 Stride 18

OpenACC OpenMP と OpenACC の違い OpenACC の指示文 アプリケーションの OpenACC 化 19

GPU を利用する方法 簡単 難しい GPU 対応ライブラリ ライブラリを呼び出すだけ ライブラリとして存在するもののみ OpenACC 既存の C/C++/Fortran プログラムに指示文を入れるだけ 細かいチューニングは不可 CUDA 自由度が最も高い 本講習会の主な対象 プログラムの大幅な書き換えを必要とする 20

OpenACC OpenACC とは アクセラレータ (GPU など ) 向けの OpenMP のよ うなもの 既存のプログラムのホットスポットに指示文を挿入し 計算の 重たい部分をアクセラレータにオフロード 対応言語 : C/C++, Fortran 指示文ベース 指示文 : コンパイラへのヒント 記述が簡便, メンテナンスなどをしやすい コードの可搬性 (portability) が高い 対応していない環境では無視される GPU プログラミング入門 C/C++ #pragma acc kernels for(i = 0;i < N;i++) {. Fortran!$acc kernels do i = 1, N. end do!$acc end kernels 21

OpenACC 規格 各コンパイラベンダ (PGI, Cray など ) が独自に実装していた拡張を統合し共通規格化 (http://www.openacc.org/) 2011 年秋に OpenACC 1.0 最新の仕様は OpenACC 2.5 対応コンパイラ 商用 :PGI, Cray, PathScale PGI は無料版も出している 研究用 :Omni (AICS), OpenARC (ORNL), OpenUH (U.Houston) フリー :GCC 6.x 開発中 ( 開発状況 : https://gcc.gnu.org/wiki/offloading) 実用にはまだ遠い 本講習会では PGI コンパイラを用いる マルチコア メニィコア並列プログラミング入門 22

OpenACC と OpenMP の実行イメージ比較 1 スレッド OpenMP OpenACC int main() { #pragma for(i = 0;i < N;i++) { CPU CPU CPU デバイス マルチコア メニィコア並列プログラミング入門 23

OpenACC と OpenMP の比較 OpenMP の想定アーキテクチャ マルチコア CPU 環境 MEMORY 計算コアが N 個 N < 100 程度 (Xeon Phi 除く ) CPU(s) 共有メモリ 計算コア 計算コア 計算コア 計算コア 計算コア 計算コア 計算コア 計算コア 一番の違いは対象アーキテクチャの複雑さ 24

OpenACC と OpenMP の比較 OpenACC の想定アーキテクチャ アクセラレータを備えた計算機環境 MEMORY ( ホスト ) CPU(s) MEMORY ( デバイス ) 計算コア N 個を M 階層で管理 N > 1000 を想定 階層数 M はアクセラレータによる ホスト - デバイスで独立したメモリ ホスト - デバイス間データ転送は低速 一番の違いは対象アーキテクチャの複雑さ 25

OpenACC と OpenMP の比較 OpenMPと同じもの Fork-Joinという概念に基づくループ並列化 OpenMPになくてOpenACCにあるもの ホストとデバイスという概念 ホスト-デバイス間のデータ転送 多階層の並列処理 OpenMPにあってOpenACCにないもの スレッドIDを用いた処理など OpenMP の omp_get_thread_num() に相当するものが無い その他 気をつけるべき違い OpenMPと比べてOpenACCは勝手に行うことが多い 転送データ 並列度などを未指定の場合は勝手に決定 マルチコア メニィコア並列プログラミング入門 26

OpenACC と OpenMP の比較デフォルトでの変数の扱い OpenMP 全部 shared OpenACC スカラ変数 : firstprivate or private 配列 : shared プログラム上の parallel/kernels 構文に差し掛かった時 OpenACC コンパイラは実行に必要なデータを自動で転送する 正しく転送されないこともある 自分で書くべき 構文に差し掛かるたびに転送が行われる ( 非効率 ) 後述の data 指示文を用いて自分で書くべき 配列はデバイスに確保される (shared 的振る舞い ) 配列変数を private に扱うためには private 指示節使う マルチコア メニィコア並列プログラミング入門 27

GPU プログラミング難易度早見表 ( 私見 ) 易 OpenACC OpenACC with Unified Memory OpenMP omp parallel do 書くだけ データマネージメントの壁 OpenACC with データ指示文 スレッド制御の壁 難 OpenACC のカーネルチューニング カーネルの CUDA 化 指示文を用いた SIMD 化 intrinsic を用いた SIMD 化 マルチコア メニィコア並列プログラミング入門 28

OpenACC の指示文 マルチコア メニィコア並列プログラミング入門 29

OpenACC の主要な指示文 並列領域指定指示文 kernels, parallel データ移動最適化指示文 data, enter data, exit data, update ループ最適化指示文 loop その他 比較的よく使う指示文 host_data, atomic, routine, declare マルチコア メニィコア並列プログラミング入門 30

並列領域指定指示文 1. 必要なデータを送る ホスト ~32GB/s デバイス 3. 計算結果を返す 2. 計算を行う ~200GB/s ~1,000GB/s メインメモリ デバイスメモリ 1, 2, 3 全てを行う指示文 マルチコア メニィコア並列プログラミング入門 31

並列領域指定指示文 :parallel, kernels アクセラレータ上で実行すべき部分を指定 OpenMP の parallel 指示文に相当 2 種類の指定方法 :parallel, kernels parallel:( どちらかというと ) マニュアル OpenMP に近い ここからここまでは並列実行領域です 並列形状などはユーザー側で指定します kernels:( どちらかというと ) 自動的 ここからここまではデバイス側実行領域です あとはお任せします 細かい指示子 節を加えていくと最終的に同じような挙動になるので どちらを使うかは好み 個人的には kernels 推奨 マルチコア メニィコア並列プログラミング入門 32

kernels/parallel 指示文 kernels parallel program main!$acc kernels do i = 1, N! loop body end do!$acc end kernels program main!$acc parallel num_gangs(n)!$acc loop gang do i = 1, N! loop body end do!$acc end parallel end program end program マルチコア メニィコア並列プログラミング入門 33

kernels/parallel 指示文 kernels parallel ホスト - デバイスを意識するのが kernels 並列実行領域であることを意識するのが parallel ホスト側 program main デバイス側 program main!$acc kernels do i = 1, N! loop body end do!$acc end kernels!$acc parallel num_gangs(n)!$acc loop gang do i = 1, N! loop body end do!$acc end parallel end program end program 並列数はデバイスに合わせてください 並列数 N でやってください マルチコア メニィコア並列プログラミング入門 34

kernels/parallel 指示文 : 指示節 kernels async wait device_type if default(none) copy parallel async wait device_type if default(none) copy num_gangs num_workers vector_length reduction private firstprivate マルチコア メニィコア並列プログラミング入門 35

kernels/parallel 指示文 : 指示節 kernels 非同期実行に用いる 実行デバイス毎にパラメータを調整 データ指示文の機能を使える parallel では並列実行領域であることを意識するため 並列数や変数の扱いを決める指示節がある parallel async wait device_type if default(none) copy num_gangs num_workers vector_length reduction private firstprivate マルチコア メニィコア並列プログラミング入門 36

kernels/parallel 実行イメージ Fortran subroutine copy(dis, src) real(4), dimension(:) :: dis, src C 言語 void copy(float *dis, float *src) { int i;!$acc kernels copy(src,dis) do i = 1, N dis(i) = src(i) end do!$acc end kernels #pragma acc kernels copy(src[0:n] dis[0:n]) for(i = 0;i < N;i++){ dis[i] = src[i]; end subroutine copy マルチコア メニィコア並列プログラミング入門 37

kernels/parallel 実行イメージ Fortran ( ホスト ) ( デバイス ) subroutine copy(dis, src) real(4), dimension(:) :: dis, src!$acc kernels copy(src,dis) do i = 1, N dis(i) = src(i) end do!$acc end kernels end subroutine copy dis, src 1dis, src の値がコピーされる 3dis _dev, src _dev の値がコピーされる dis, src 0dis, src の領域が確保される dis_dev, src_dev 2 デバイス上の計算 dis _dev, src _dev 4dis, src の領域が解放される マルチコア メニィコア並列プログラミング入門 38

デバイス上で扱われるべきデータについて プログラム上の parallel/kernels 構文に差し掛かった時 OpenACC コンパイラは実行に必要なデータを自動で転送する 正しく転送されないこともある 自分で書くべき 構文に差し掛かるたびに転送が行われる ( 非効率 ) 後述の data 指示文を用いて自分で書くべき 自動転送は default(none) で抑制できる スカラ変数は firstprivate として扱われる 指示節により変更可能 配列はデバイスに確保される (shared 的振る舞い ) 配列変数をスレッドローカルに扱うためには private を指定する マルチコア メニィコア並列プログラミング入門 39

データ移動最適化指示文 1. 必要なデータを送る ホスト ~32GB/s デバイス 3. 計算結果を返す 2. 計算を行う ~200GB/s ~1,000GB/s メインメモリ デバイスメモリ 1, 3 を最適化するための指示文 ただしデータの一貫性を維持するのはユーザの責任 マルチコア メニィコア並列プログラミング入門 40

データ移動最適化指示文 :data, enter/exit data デバイス側で必要なデータと範囲を指定 Allocate, Memcpy, Deallocate を行う data 指示文 ( 推奨 ) Allocate + Memcpy (H D) + Deallocate 構造ブロックに対してのみ適用可 コードの見通しが良い enter data 指示文 Allocate + Memcpy (H D) exit data とセット 構造ブロック以外にも使える exit data 指示文 Memcpy (H D) + Deallocate enter data とセット 構造ブロック以外にも使える マルチコア メニィコア並列プログラミング入門 41

データ移動最適化指示文が必要なとき Fortran subroutine copy(dis, src) real(4), dimension(:) :: dis, src do j = 1, M!$acckernels copy(src,dis) do i = 1, N dis(i) = dis(i) + src(i) end do!$acc end kernels end do end subroutine copy C 言語 void copy(float *dis, float *src) { int i, j; for(j = 0;j < M;j++){ #pragma acckernels copy(src[0:n] dis[0:n]) for(i = 0;i < N;i++){ dis[i] = dis[i] + src[i]; Kernels をループで囲むとどうなるか マルチコア メニィコア並列プログラミング入門 42

データ移動最適化指示文が必要なとき C 言語 ~200GB/s for(j = 0;j < M;j++){ ホスト メインメモリ 1. 必要なデータを送る ~32GB/s 3. 計算結果を返す ~1,000GB/s デバイス デバイスメモリ 1, 2, 3 全てが繰り返される! 2. 計算を行う void copy(float *dis, float *src) { int i, j; for(j = 0;j < M;j++){ #pragma acckernels copy(src[0:n] dis[0:n]) for(i = 0;i < N;i++){ dis[i] = dis[i] + src[i]; Kernels をループで囲むとどうなるか マルチコア メニィコア並列プログラミング入門 43

data 指示文 Fortran subroutine copy(dis, src) real(4), dimension(:) :: dis, src!$acc data copy(src,dis) do j = 1, M!$acckernels present(src,dis) do i = 1, N dis(i) = dis(i) + src(i) end do!$acc end kernels end do!$acc end data end subroutine copy present: 既に転送済であることを示す (OpenACC2.5 の仕様以降 copy は present_or_copy として扱われることになったので 実は書き換えなくても大丈夫になった ) C 言語 void copy(float *dis, float *src) { int i, j; #pragma acc data copy(src[0:n] dis[0:n]) { for(j = 0;j < M;j++){ #pragma acckernels present(src,dis) for(i = 0;i < N;i++){ dis[i] = dis[i] + src[i]; C の場合 data 指示文の範囲は { で指定 ( この場合は for が構造ブロックになってるので なくても大丈夫だが ) マルチコア メニィコア並列プログラミング入門 44

~200GB/s data 指示文 ホスト メインメモリ 1. 必要なデータを送る ~32GB/s 3. 計算結果を返す ~1,000GB/s for(j = 0;j < M;j++){ デバイス デバイスメモリ 2 のみが繰り返される! 2. 計算を行う C 言語 void copy(float *dis, float *src) { int i, j; #pragma acc data copy(src[0:n] dis[0:n]) { for(j = 0;j < M;j++){ #pragma acckernels present(src,dis) for(i = 0;i < N;i++){ dis[i] = dis[i] + src[i]; C の場合 data 指示文の範囲は { で指定 ( この場合は for が構造ブロックになってるので なくても大丈夫だが ) マルチコア メニィコア並列プログラミング入門 45

enter data, exit data 指示文 void main() { double *q; int step; for(step = 0;step < N;step++){ if(step == 0) init(q); solvera(q); solverb(q);. if(step == N) fin(q); void init(double *q) { q = (double *)malloc(sizeof(double)*m); q = ; // 初期化 #pragma acc enter data copyin(q[0:m]) void fin(double *q) { #pragma acc exit data copyout(q[0:m]) print(q); // 結果出力 free(q); マルチコア メニィコア並列プログラミング入門 46

data, enter/exit data 指示文の指示節 data if copy copyin copyout create present present_or_... deviceptr CUDA などと組み合わせる時に利用 cudamalloc などで確保済みのデータを指定し OpenACC で扱い可とする enter data if async 非同期転送用 wait copyin create present_or_... exit data if async wait copyout delete マルチコア メニィコア並列プログラミング入門 47

data, enter/exit data 指示文の指示節 copy allocate, memcpy (H D), memcpy (D H), deallocate copyin allocate, memcpy (H D), deallocate 結果の出力を行わない copyout allocate, memcpy (D H), deallocate データの入力を行わない create allocate, deallocate コピーは行わない present 何もしない 既にデバイス上にあることを教える present_or_copy/copyin/copyout/create ( 省略形 :pcopy) デバイス上になければ copy/copyin/copyout/create する あれば何もしないただしOpenACC2.5 以降では copy, copyin, copyout の挙動は pcopy, pcopyin, pcopyout と同一 マルチコア メニィコア並列プログラミング入門 48

データ移動指示文 : データ転送範囲指定 送受信するデータの範囲の指定 部分配列の送受信が可能 注意 :FortranとCで指定方法が異なる 二次元配列 A を転送する例 Fortran 版!$acc data copy(a(lower1:upper1, lower2:upper2) ) fortranでは下限と上限を指定!$acc end data C 版 #pragma acc data copy(a[start1:length1][start2:length2]) Cでは先頭と長さを指定 #pragma acc end data マルチコア メニィコア並列プログラミング入門 49

データ移動指示文 :update 指示文 データ指示文などで既にデバイス上に確保済みのデータを対象とする Memcpy (H D) の機能を持っていると思えば良い!$acc data copy( A(:,:) ) do step = 1, N!$acc update host( A(1:2,:) ) call comm_boundary( A )!$acc update device( A(1:2,:) ) end do!$acc end data update if async wait device_type self #host と同義 host # H D device # H D マルチコア メニィコア並列プログラミング入門 50

ループ最適化指示文 1. 必要なデータを送る ホスト ~32GB/s デバイス 3. 計算結果を返す 2. 計算を行う ~200GB/s ~1,000GB/s メインメモリ デバイスメモリ 2 の最適化を行う指示文 マルチコア メニィコア並列プログラミング入門 51

階層的並列モデルとループ指示文 OpenACC ではスレッドを階層的に管理 gang, worker, vector の 3 階層 gang:worker の塊一番大きな単位 worker:vector の塊 vector: スレッドに相当する一番小さい処理単位 loop 指示文 parallel/kernels 中のループの扱いについて指示 パラメータの設定はある程度勝手にやってくれる 粒度 (gang, worker, vector) の指定 ループ伝搬依存の有無の指定 GPUでの行列積の例!$acc kernels!$acc loop gang do j = 1, n!$acc loop vector do i = 1, n cc = 0!$acc loop seq do k = 1, n cc = cc + a(i,k) * b(k,j) end do c(i,j) = cc end do end do!$acc end kernels マルチコア メニィコア並列プログラミング入門 52

階層的並列モデルとアーキテクチャ OpenMP は 1 階層 マルチコア CPU も 1 階層 最近は 2 階層目 (SIMD) がある NVIDIA GPU の構成 GPU デバイスメモリ CUDA は block と thread の 2 階層 NVIDA GPU も 2 階層 1 SMX に複数 CUDA core を搭載 各コアは SMX のリソースを共有 OpenACC は 3 階層 様々なアクセラレータに対応するため SMX CUDA コア マルチコア メニィコア並列プログラミング入門 53 53

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction マルチコア メニィコア並列プログラミング入門 54

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction 3 つのループが一重化される!$acc kernels!$acc loop collapse(3) gang vector do k = 1, 10 do j = 1, 10 do i = 1, 10. end do end do end do!$acc end kernels 並列化するにはループ長の短すぎるループに使う マルチコア メニィコア並列プログラミング入門 55

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction!$acc kernels!$acc loop gang(n) do k = 1, N!$acc loop worker(1) do j = 1, N!$acc loop vector(128) do i = 1, N.!$acc kernels!$acc loop gang vector(128) do i = 1, N. 数値の指定は難しいので 最初はコンパイラ任せでいい vector は worker より内側 worker は gang より内側 ただし 1 つのループに複数つけるのは OK マルチコア メニィコア並列プログラミング入門 56

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction B に間接参照 do j = 1, N do i = 1, N idxi(i) = i; idxj(j) = j end do end do!$acc kernels &!$acc& copyin(a, idxi, idxj) copyout(b)!$acc loop independent gang do j = 1, N!$acc loop independent vector(128) do i = 1, N B(idxI(i),idxJ(j)) = alpha * A(i,j) end do end do!$acc end kernels OpenACC コンパイラは保守的 依存関係が生じそうなら並列化しない きちんと並列化されているかどうか 必ずコンパイラのメッセージを確認する ( やり方は後述 ) マルチコア メニィコア並列プログラミング入門 57

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction!$acc kernels &!$acc loop reduction(+:val) do i = 1, N val = val + 1 end do!$acc end kernels acc reduction (+:val) 演算子 対象とする変数制限 : スカラー変数のみ 簡単なものであれば PGI コンパイラは自動で reduction を入れてくれる 利用できる演算子 (OpenACC2.0 仕様書より ) マルチコア メニィコア並列プログラミング入門 58

参考 : リダクションで OpenACC が好きになる そもそもリダクションって? sum = 0.0 リダクションが必要な例 for(i = 0;i < N;i++) sum += array[i] スレッド 1 スレッド 2 スレッド 3 array 1 2 3 4 5 6 7 8 9 6 15 24 6 15 24 1. 各スレッドが担当領域で縮約 2. 一時配列に書き込む 3. 一時配列を縮約 sum = 45 マルチコア メニィコア並列プログラミング入門 59

参考 : リダクションで OpenACC が好きになる CUDA で実装しようと思うと 全部自分でやる これは shuffle 機能を使ったリダクション int main( int argc, char* argv[] ){. double *tmp,*ans_d; cudamalloc((void**)&tmp, sizeof(double) * 896); cudamalloc((void**)&ans_d, sizeof(double) * 1); ホスト側プログラム 一時配列の確保 int chunk = (N+895)/896; dim3 dimgrid_l(896, 1, 1); 使用するスレッド数の宣言 dim3 dimblock_l(128, 1, 1); dim3 dimgrid_g(1, 1, 1); dim3 dimblock_g(1024, 1, 1); GPUカーネル呼び出し reduction_l <<<dimgrid_l, dimblock_l>>> (N,A_d,tmp,chunk); reduction_g <<<dimgrid_g, dimblock_g>>> (tmp,ans_d); cudamemcpy(&sum,ans_d,sizeof(double),cudamemcpydevicetohost);. 結果の書き戻し global void reduction_l(int N, double *A, double *tmp, int chunk){ shared double sum_tmp[4]; int tx = threadidx.x; int bx = blockidx.x; int st = bx*chunk + tx; int en = min(n,(bx+1)*chunk); double sum = 0.0; for(i = st;i < en;i+=128) sum += A[i]; sum += shfl_xor(sum,16); sum += shfl_xor(sum,8); sum += shfl_xor(sum,4); sum += shfl_xor(sum,2); sum += shfl_xor(sum,1); if(tx % 32 == 0) sum_tmp[tx/32] = sum; syncthreads(); if(tx == 0) tmp[bx] = sum + sum_tmp[1] + sum_tmp[2] + sum_tmp[3]; GPU カーネル 1 global void reduction_g(double *tmp, double *ans){ shared double sum_tmp[32]; double sum; int tx = threadidx.x; if(tx >= 896) sum = 0.0; 一時配列も同様に縮約 else sum = tmp[tx]; sum += shfl_xor(sum,16); sum += shfl_xor(sum,8); sum += shfl_xor(sum,4); sum += shfl_xor(sum,2); sum += shfl_xor(sum,1); if(tx % 32 == 0) sum_tmp[tx/32] = sum; syncthreads(); if(tx < 32){ sum = sum_tmp[tx]; sum += shfl_xor(sum,16); sum += shfl_xor(sum,8); sum += shfl_xor(sum,4); sum += shfl_xor(sum,2); sum += shfl_xor(sum,1); syncthreads(); if(tx == 0) ans[0] = sum; Warp shuffle 機能を使った Warp 内の縮約 shared memory を使った Warp 間の縮約 syncthreads() による同期必須 GPU カーネル 2 マルチコア メニィコア並列プログラミング入門 60

参考 : リダクションで OpenACC が好きになる OpenACC なら sum = 0.0 #pragma acc kernels copyin(a[0:n]) #pragma acc loop reduction(+:sum) for(i = 0;i < N;i++){ sum += array[i] これだけ! 性能も 1. OpenACC のリダクション 2. 一旦 CPU に書き戻して 1 スレッドで計算 3. CPU に書きもどさず GPU の 1 スレッドで計算 4. CUDA の shuffle を使ったリダクション array( 倍精度 ) のサイズ 100000000 の時 (Fortran) 1. reduction acc time : 0.00148 (s) 2. copyback and CPU time: 0.63831 (s) 3. cuda 1 thread time : 9.63381 (s) 4. reduction cuda time : 0.00140 (s) 下手なプログラムを書くくらいなら OpenACC に任せた方が速い! マルチコア メニィコア並列プログラミング入門 61

関数呼び出し指示文 :routine parallel/kernels 領域内から関数を呼び出す場合 routine 指示文を使う #pragma acc routine vector プロトタイプ宣言にもつける extern double vecsum(double *A); #pragma acc parallel num_gangs(n) vector_length(128) for (int i = 0;i < N; i++){ #pragma acc routine vector max = vecsum(a[i*n]); double vecsum(double *A){ double x = 0; #pragma acc loop reduction(+:x) for(int j = 0;j < N;j++){ x += A[j]; return x; マルチコア メニィコア並列プログラミング入門 62

host_data 指示文 OpenACC のデータ指示文を使うと subroutine copy(dis, src) real(4), dimension(:) :: dis, src!$acc data copy(src,dis)!--------------------------! CPU 上の処理!-------------------------- call hoge(dis,src)!$acckernels present(src,dis) do i = 1, N dis(i) = dis(i) + src(i) end do!$acc end kernels ( ホスト ) ( デバイス ) dis, src 1 両方に作られる dis_dev, src_dev 2 並列領域以外ではホスト側が使われる関数にもホスト側のアドレスが渡る 3 並列領域ではデバイス側が使われる!$acc end data end subroutine copy マルチコア メニィコア並列プログラミング入門 デバイス側のアドレスを渡したい時は?? 63

host_data 指示文 Fortran 並列領域の外でデバイス側のアドレスを使うための指示文 データ指示文で確保済の配列が対象 デバイス側のアドレスを使いたいケースって? CUDA で書いた関数の呼び出し GPU 用のライブラリの呼び出し GPU Direct による MPI 通信 GPU Direct: ホスト側のメモリを介さず GPU 間で直接 MPI によるデータ通信をするもの C!$acc data create(tmp) copy(val) copyin(a)...!$acc host_data use_device(a,tmp,val) call reduction_cuda_shuffle(val,n,a,tmp)!$acc end host_data...!$acc end data #pragma acc data create(tmp[0:896]) copy(val) {... #pragma acc host_data use_device(a,tmp,val) { reduction_cuda_shuffle(&val,n,a,tmp);... 領域内ではデバイス側のアドレスが使われるマルチコア メニィコア並列プログラミング入門 64

atomic 指示文 並列化領域内で どうしても並列化できない部分が存在する場合に使う 全体的には並列に計算できるが 書き込み先が衝突するようなケース Pascal GPU は倍精度の atomicadd をハードウェアサポートしてるので それなりに速い 一文ずつ囲む!$acc kernels async(0)!$acc loop independent gang vector do k= inls,inle i= IAL(k) X1= Xin(3*i-2) X2= Xin(3*i-1) X3= Xin(3*i ) WVAL1= AL(k,1)*X1 + AL(k,2)*X2 + AL(k,3)*X3 WVAL2= AL(k,4)*X1 + AL(k,5)*X2 + AL(k,6)*X3 WVAL3= AL(k,7)*X1 + AL(k,8)*X2 + AL(k,9)*X3 i = INL_G(k)!$acc atomic tmpl(i,1) = tmpl(i,1) + WVAL1!$acc end atomic!$acc atomic tmpl(i,2) = tmpl(i,2) + WVAL2!$acc end atomic!$acc atomic tmpl(i,3) = tmpl(i,3) + WVAL3!$acc end atomic enddo!$acc end kernels i が関節参照なので 書き込み先が衝突する可能性がある マルチコア メニィコア並列プログラミング入門 65

OpenACC と Unified Memory Unified Memory とは 物理的に別物の CPU と GPU のメモリをあたかも一つのメモリのように扱う機能 Pascal GPU ではハードウェアサポート ページフォルトが起こると勝手にマイグレーションしてくれる OpenACC と Unified Memory OpenACC に Unified Memory を直接使う機能はない PGI コンパイラではオプションを与えることで使える pgfortran acc ta=tesla,managed 使うとデータ指示文が無視され 代わりに Unified Memory を使う マルチコア メニィコア並列プログラミング入門 66

Unified Memory のメリット デメリット メリット データ移動の管理を任せられる 複雑なデータ構造を簡単に扱える デメリット 本来はメモリ空間が分かれているため ディープコピー問題が発生する!$acc kernels!$acc loop independent do il=1,kt!$acc loop independent do it=1,ndt; itt=it+nstrtt-1 zbu(il)=zbu(il)+st_leafmtxp%st_lf(ip)%a1(it,il)*zu(itt) enddo enddo!$acc end kernels ページ単位で転送するため 細かい転送が必要な場合には遅くなる CPU 側のメモリ管理を監視しているので allocate, deallocate を繰り返すアプリでは CPU 側が極端に遅くなる 今研究で使っているコードでは 20 倍近く遅くなった こう書くだけで正しく動くのは 従来の CUDA ユーザーからすると革命的 マルチコア メニィコア並列プログラミング入門 67

アプリケーションの移植方法 マルチコア メニィコア並列プログラミング入門 68

アプリケーションの OpenACC 化手順 1. プロファイリングによるボトルネック部位の導出 2. ボトルネック部位の OpenACC 化 1. 並列化可能かどうかの検討 2. (OpenACC の仕様に合わせたプログラムの書き換え ) 3. parallel/kernels 指示文適用 3. data 指示文によるデータ転送の最適化 4. OpenACC カーネルの最適化 1 ~ 4 を繰り返し適用 それでも遅ければ 5. カーネルの CUDA 化 スレッド間の相互作用が多いアプリケーションでは shared memory や shuffle 命令を自由に使える CUDA の方が圧倒的に有利 マルチコア メニィコア並列プログラミング入門 69

既に OpenMP 化されているアプリケーションの OpenACC 化手順 1.!$omp parallel を!$acc kernels に機械的に置き換え 2. (Unified Memory を使い とりあえず GPU 上で実行 ) 本講習会では扱いません 3. データ指示文を用いて転送の最適 4. コンパイラのメッセージを見ながら OpenACC カーネルの最適化 5. カーネルの CUDA 化など スレッド間の相互作用が多いアプリケーションでは shared memory や shuffle 命令を自由に使える CUDA の方が圧倒的に有利 マルチコア メニィコア並列プログラミング入門 70

データ指示文による最適化手順 int main(){ double A[N]; sub1(a); sub2(a); sub3(a); sub1 main sub2 sub3 ホスト デバイス sub2(double A){ suba(a); subb(a); suba subb suba(double A){ for( i = 0 ~ N ) { 葉っぱの部分から OpenACC 化を始める マルチコア メニィコア並列プログラミング入門 71

データ指示文による最適化手順 int main(){ double A[N]; sub1(a); sub2(a); sub3(a); sub1 main sub2 sub3 ホスト デバイス sub2(double A){ suba(a); subb(a); suba data 指示文で配列 A をコピー subb suba suba(double A){ #pragma acc for( i = 0 ~ N ) { この状態でも必ず正しい結果を得られるように作る! この時 速度は気にしない! マルチコア メニィコア並列プログラミング入門 72

データ指示文による最適化手順 int main(){ double A[N]; sub1(a); #pragma acc data { sub2(a); sub3(a); sub1 main sub2 ホストデバイス data 指示文で配列 Aをコピー sub3 sub2 sub2(double A){ suba(a); subb(a); suba subb suba subb suba(double A){ #pragma acc for( i = 0 ~ N ) { 徐々にデータ移動を上流に移動する マルチコア メニィコア並列プログラミング入門 73

データ指示文による最適化手順 int main(){ double A[N]; #pragma acc data { sub1(a); sub2(a); sub3(a); sub1 main sub2 ホストデバイス data 指示文で配列 Aをコピー sub3 sub1 main sub2 sub3 sub2(double A){ suba(a); subb(a); suba subb suba subb suba(double A){ #pragma acc for( i = 0 ~ N ) { ここまで来たら ようやく個別のカーネルの最適化を始める データの転送時間が相対的に十分小さくなればいいので かならずしも最上流までやる必要はない マルチコア メニィコア並列プログラミング入門 74

上で説明した指示文を用いて OpenACC 化したもの OpenACC の適用例 75

OpenACC を用いた ICCG 法ソルバーの Pascal GPU における性能評価 と KNL 星野哲也 大島聡史 塙敏博 中島研吾 伊田明弘 1) 東京大学情報基盤センター 最先端共同 HPC 基盤施設 (JCAHPC) 2) 第 158 回 HPC 研究会 @ 熱海

メニーコアプロセッサと消費電力 Green500 Top10 (2016/11) は全部メニーコア メニーコア向けのアルゴリズムや最適化手法の開発が重要 P100 P100 PEZY Sunway KNL KNL KNL KNL KNL KNL 77

本研究の概要 研究目的 最新世代の GPU,Xeon Phi である P100 と KNL の性能評価 評価手法 OpenACC, OpenMPにより並列化したICCG 法ソルバーを使用 P100とKNLの性能を比較 前世代 (Kepler, KNC) と比較 それぞれへの最適化の適用 成果 P100 において 指示文の最適化などにより 1.21 倍の性能向上を達成 KNL において 同期の削減などにより 1.20 倍の性能向上を達成 78

発表概要 研究背景 対象アプリケーション ICCG 法 行列格納手法 OpenACCによる並列化 P100 vs KNL ( vs BDW, K20, KNC) P100 KNL 向け最適化 まとめ 今後の課題 79

対象アプリケーション 有限体積法, 一様場ポアソン方程式ソルバー [ 大島他 SWoPP 2014], [ 中島 HPC-139], [ 中島 HPC-147], [ 中島 HPC-157] 差分格子 : データ構造は非構造,7 点ステンシル, 問題サイズ 128 3 対称正定な疎行列を係数とする連立一次方程式 ICCG 法, 上下三角成分を別々に格納 色付け リオ - ダリング CM-RCM + Coalesced/Sequential NZ DZ 行列格納手法 CRS, Sliced-ELL, Sell-C-σ z y x NX NY DX DY 80

ICCG 法 共役勾配法 (Conjugate Gradient, CG) 前処理 不完全コレスキー分解 (Incomplete Cholesky Factorization, IC) 並列化にはカラーリングが必要 Compute r (0) = b-[a]x (0) for i= 1, 2, solve [M]z (i-1) = r (i-1) r i-1 = r (i-1) z (i-1) if i=1 p (1) = z (0) else b i-1 = r i-1 /r i-2 p (i) = z (i-1) + b i-1 endif q (i) = [A]p (i) a i = r i-1 /p (i) q (i) x (i) = x (i-1) + a i p (i) r (i) = r (i-1) - a i q (i) check convergence r end p (i-1) 81

ICCG 法並列化 : データ依存性の解決 色づけ + リオ - ダリング による並列性抽出 同色内は並列に実行可能 色数 収束性並列性同期コスト 29 45 22 61 16 46 11 62 47 7 63 4 48 2 64 1 29 22 16 11 7 4 2 1 53 36 19 2 49 33 17 1 37 49 13 30 29 23 51 14 17 30 12 53 15 31 8 55 16 5 32 3 37 30 23 17 12 8 5 3 7 54 37 20 3 50 34 18 44 41 38 57 31 42 24 58 18 43 13 59 44 9 60 6 44 38 31 24 18 13 9 6 25 8 55 38 21 4 51 35 50 33 9 45 25 39 35 10 32 26 25 37 11 19 27 14 39 12 10 28 50 45 39 32 25 19 14 10 43 26 9 56 39 22 5 52 55 37 51 53 46 38 40 54 33 39 26 55 20 40 15 56 55 51 46 40 33 26 20 15 61 44 27 10 57 40 23 6 59 17 5 56 21 52 19 6 47 22 41 21 7 34 23 27 23 8 21 24 59 56 52 47 41 34 27 21 14 62 45 28 11 58 41 24 62 33 60 49 57 34 53 50 48 35 42 51 35 36 28 52 62 60 57 53 48 42 35 28 31 15 63 46 29 12 59 42 64 1 63 17 61 32 58 18 54 53 49 19 43 74 36 20 64 63 61 58 54 49 43 36 48 32 16 64 47 30 13 60 MC (Color#=4) Multicoloring RCM Reverse Cuthill-Mckee CM-RCM (Color#=4) Cyclic MC + RCM 82

ICCG 法並列化 : データ依存性の解決 MC 並列性良 悪条件問題に弱い RCM 収束性良 並列性悪 同期コスト高 CM-RCM 並列性 収束性 本発表で使用 29 45 22 61 16 46 11 62 47 7 63 4 48 2 64 1 29 22 16 11 7 4 2 1 53 36 19 2 49 33 17 1 37 49 13 30 29 23 51 14 17 30 12 53 15 31 8 55 16 5 32 3 37 30 23 17 12 8 5 3 7 54 37 20 3 50 34 18 44 41 38 57 31 42 24 58 18 43 13 59 44 9 60 6 44 38 31 24 18 13 9 6 25 8 55 38 21 4 51 35 50 33 9 45 25 39 35 10 32 26 25 37 11 19 27 14 39 12 10 28 50 45 39 32 25 19 14 10 43 26 9 56 39 22 5 52 55 37 51 53 46 38 40 54 33 39 26 55 20 40 15 56 55 51 46 40 33 26 20 15 61 44 27 10 57 40 23 6 59 17 5 56 21 52 19 6 47 22 41 21 7 34 23 27 23 8 21 24 59 56 52 47 41 34 27 21 14 62 45 28 11 58 41 24 62 33 60 49 57 34 53 50 48 35 42 51 35 36 28 52 62 60 57 53 48 42 35 28 31 15 63 46 29 12 59 42 64 1 63 17 61 32 58 18 54 53 49 19 43 74 36 20 64 63 61 58 54 49 43 36 48 32 16 64 47 30 13 60 MC (Color#=4) Multicoloring RCM Reverse Cuthill-Mckee CM-RCM (Color#=4) Cyclic MC + RCM 83

Coalesced, Sequential オーダリング 色順に並べ替えた後 Coalesced データの順はそのまま 各スレッドは不連続なメモリアクセス Sequential データ順を再オーダリング 計算単位 ( スレッドやコア ) 内で連続アクセス Coalesced Coloring (5 colors) +Ordering Sequential Coloring (5 colors) +Ordering 各スレッド上で不連続なメモリアクセス ( 色の順に番号付け ) color=1 color=2 color=3 color=4 color=5 color=1 color=2 color=3 color=4 color=5 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 Initial Vector color=1 color=2 color=3 color=4 color=5 color=1 color=2 color=3 color=4 color=5 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 Initial Vector 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 8 8 8 スレッド内で連続に番号付け 84

行列格納方法 一番一般的な格納方法 0 で padding 非零要素数でソート 複数配列で表現することで無駄な計算を削減 C を SIMD 長等に合わせ 適宜 0 padding で整形 s C CRS ELL Sliced ELL SELL-C-s (SELL-2-8) 85

実施ケース 略称 Numbering 格納形式 c-crs Coalesced CRS c-sliced-ell Sliced-ELL ( ブロッキングあり ) c-sell-c-σ s-crs Sequential CRS SELL-C-σ s-sliced-ell Sliced-ELL ( ブロッキングあり ) s-sell-c-σ カラーリングはいずれも CM-RCM 問題サイズは 128 3 SELL-C-σ 86

OpenACC/OpenMP 実装 同一ソースコードにそれぞれの指示文を挿入 HPC157 から 不要な一時配列の除去などの最適化 HPC157 から 一部最適化を評価のため除外 (OpenMP) OpenACC 全並列化ループに!$acc kernels を適用 データ転送は計測外で行う 予稿から最適化レベルを1 段階ダウン OpenMP async 指示節を除外 全並列化ループに!$omp parallel do を適用!$omp parallel do private(... )!$acc kernels!$acc loop independent gang do ip= 1, PEsmpTOT ip1= (ip-1)*ncolortot + ic!$omp simd!$acc loop independent vector do i= index(ip1-1)+1, index(ip1) enddo enddo!$omp end parallel do!$acc end kernels 87

Hardware P100: NVIDIA Tesla P100 ( 予稿はPCI-E 版 ) Reedbush-H K20: NVIDIA Tesla K20Xm TSUBAME2.5 KNC: Intel Xeon Phi 5110P (Knights Corner) KNSC KNL: Intel Xeon Phi 7250 (Knights Landing) Oakforest-PACS BDW: Intel Xeon E5-2695 v4 (Intel Broadwell-EP) Reedbush-U 88

Hardware spec 略称 P100 K20 KNL KNC BDW 動作周波数 (GHz) 1.480 0.732 1.40 1.053 2.10 コア数 ( 最大有効スレッド数 ) 理論演算性能 (GFLOPS) 1,792 896 68 (272) 60 (240) 18 (18) 5,304 1,311.7 3,046.4 1,010.9 604.8 主記憶容量 (GB) 16 6 16 8 128 メモリバンド幅 (GB/sec., Stream Triad) System Reedbush-H TSUBAME2.5 534 179 490 159 65.5 Oakforest- PACS KNSC Reedbush-U 89

コンパイラ 環境変数 P100 コンパイラ & オプション pgfortran 17.1 ( 予稿は 16.10) -O3 ta=tesla:cc60 環境変数 特になし 90

コンパイラ 環境変数 KNL その他 numactl membind=1 で MCDRAM を使用 メモリモデル :Flat サブ NUMA モード :Quadrant コンパイラ & オプション ifort (IFORT) 17.0.0 20160721 -align array64byte -O3 -xmic-avx512 -qopenmp -qopt-streamingstores=always -qopt-streaming-cache-evict=0 ただし CRS を使うときは -qopt-streaming-stores=never ( 予稿では always) 環境変数 export OMP_STACKSIZE=1G ulimit -s 1000000 66 スレッドの時 export OMP_NUM_THREADS=66 export KMP_AFFINITY=granularity=fine,proclist=[2-67],explicit 132/198/264 スレッドの時 export OMP_NUM_THREADS=132/198/264 export KMP_AFFINITY=compact export KMP_HW_SUBSET=66c@2,2t/3t/4t 91

P100 1.1 Coalesced 版が速い Sell-C-σ(C=64, σ=1) と Sliced-ELL が同程度で最速 CRS が 3 割程度遅い 色数 6 程度が最速 原因 : 同期コスト ( カーネル呼び出し ) 予稿と違うのは async 外したため Elapsed time [sec] 1.0 0.9 0.8 0.7 0.6 0.5 2 4 6 8 10 12 14 16 18 20 Color# c-crs c-sliced-ell c-sell-64-1 s-crs s-sliced-ell s-sell-64-1 92

KNL Sequential 版が速い Sell-C-σ(C=8, σ=1) が最速 CRS が 2.5 倍程度遅い 色数 14 程度が最速 66/132/198/264 スレッド中 66 スレッドが最速 Elapsed time [sec] 2.5 2.3 2.1 1.9 1.7 1.5 1.3 1.1 0.9 0.7 0.5 2 4 6 8 10 12 14 16 18 20 Color# c-crs c-sliced-ell c-sell-8-1 s-crs s-sliced-ell s-sell-8-1 93

各プロセッサの性能比較 各プロセッサ最速の疎行列形状の結果 K20 vs KNC KNC が 1.57 倍も遅い P100 vs KNL KNL が 1.10 倍しか遅くない P100 vs K20 P100 は K20 の 2.71 倍程度 Elapsed time [sec] 4.5 4.0 3.5 3.0 2.5 2.0 1.5 1.0 0.5 0.0 BDW KNC K20 KNL P100 2 4 6 8 10 12 14 16 18 20 Color# P100 K20 KNL KNC BDW 94

実行効率 (vs. BDW) 色数 10 の時の比較 メモリバンド幅 X 倍で性能も X 倍になると仮定すると P100 KNL の実行効率は BDW の 61% 程度 略称 P100 K20 KNL KNC BDW メモリバンド幅 (GB/sec., Stream Triad) 534 179 490 159 65.5 相対メモリバンド幅 8.152 2.733 7.481 2.427 1 実行時間 0.6448 1.714 0.7061 2.708 3.224 相対性能 5.00 1.88 4.57 1.19 1 ( 相対性能 )/( 相対メモリバンド幅 ) 0.613 0.688 0.610 0.490 1 95

最適化 (OpenACC) 1. Baseline 全並列ループに!$acc kernels 2. Async ( 予稿のBaseline)!$acc kernels を!$acc kernels async(0) と置き換え 3. Thread gang, vector などのパラメータを調整 4. Fusion SPMV 部分のカーネルを融合 指示文のみの変更 コードの変更 96

最適化 3 Thread リダクション部分のスレッド数を調整 目的 : ローカルリダクションで生成される一時配列のサイズを小さくする デフォルト設定 ローカルリダクション gang = (N-1)/128+1 vector = 128 グローバルリダクション gang = 1 vector = 256!$omp parallel do private(i) reduction(+:rho)!$acc kernels async(0)!$acc loop independent gang(448) vector reduction(+:rho) do i= 1, N RHO= RHO + W(i,R)*W(i,Z) enddo!$acc end kernels 16384 要素の一時配列ができ その配列を 1 gang でリダクション 448 要素の一時配列ができ その配列を 1 gang でリダクション 97

最適化 4 Fusion カーネルを融合し 呼び出しコストを削減 SpMV 部分では 色 1, 色 2~N-1, 色 N の時で処理内容が異なる 色 2~N-1 のカーネルを N-2 回呼び出す 色 2~N-1 の処理を 複数のカーネルから 1 カーネルに融合!C!C SpMV!C do ic = 1, NCOLORtot if( ic == 1 ) then! 色 1 の時の処理! 色ループ else if ( 2 <= ic, ic < NCOLORtot) then else endif enddo! 色 2 ~ 色 NCOLORtot-1 の時の処理! 色 NCOLORtot の時の処理 98

P100 最適化結果 0.75 async の効果大 カーネル起動オーバーヘッドを隠せる Baseline から 1.21 倍の性能向上 BDW 比実行効率 61.3% 74.5% Elapsed time [sec] 0.70 0.65 0.60 0.55 0.50 2 4 6 8 10 12 14 16 18 20 Color# 1 Baseline 2 Async 3 Thread 4 Fusion 99

最適化 (OpenMP) 1. Baseline 全並列ループに!$omp parallel do 2. mvparallel1!$omp parallel を色ループの外に 3. nowait!$omp end do nowait を使う (SpMV 部分 ) 4. mvparallel2!$omp parallelを収束判定ループの外に 5. rmompdo!$omp doを使わず自力で ( 機械的に ) ループ分割 (reduction 以外 ) 6. rmreduction reductionを自分で書く 7. loopschedule ループスケジューリングの変更 指示文のみの変更 コードの変更 100

Move!$omp paralell Baseline do L= 1, ITR! 収束判定ループ do ic = 1, NCOLORtot! 色ループ!$omp parallel do do... end do!$omp end parallel do end do enddo do L= 1, ITR! 収束判定ループ!$omp parallel do ic = 1, NCOLORtot! 色ループ!$omp do do... end do!$omp end do end do!$omp end parallel enddo 2 mvparallel 4 mvparallel!$omp parallel do L= 1, ITR! 収束判定ループ do ic = 1, NCOLORtot! 色ループ!$omp do do... end do!$omp end do end do enddo!$omp end parallel

ループスケジューリング Sequential Initial Vector 5 rmompdo 自力で機械的に static 分割 7 loopscheduling リオーダリングの知識を使い 前進後退代入などと同一領域を担当 Coloring (5 colors) +Ordering 各スレッド上で不連続なメモリアクセス ( 色の順に番号付け ) color=1 color=2 color=3 color=4 color=5 color=1 color=2 color=3 color=4 color=5 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 8 8 8 スレッド内で連続に番号付け Baseline!$omp do do i= 1, N W(i) =... enddo!$omp end do! この後 W(i) を前進後退代入の入力! として使う 5 rmompdo 7 loopscheduling ip = omp_get_thread_num()+1 nth = omp_get_num_threads() ls = (N+nth-1)/nth do i= (ip-1)*ls+1, min(ip*ls,n) do i = 自スレッドの担当部分 W(i) =... W(i) =... enddo enddo!$omp barrier! バリアはいらない

自力リダクション C1= 0.d0!$omp do reduction(+:c1) do i= 1, N C1= C1 + W(i,P)*W(i,Q) enddo!$omp end do ALPHA= RHO / C1 オリジナルはバリア 2 回 ローカルリダクションとグローバルリダクションの間!$omp end do の時 nowait は不可!$omp parallel private(i, ALPHA, C1,...) ip = omp_get_thread_num()+1 nth = omp_get_num_threads() ls = (N+nth-1)/nth... C1S(ip)= 0.0d0 do i= (ip-1)*ls+1, min(ip*ls,n) C1S(ip)= C1S(ip) + W(i,P)*W(i,Q) enddo C1= 0.d0!$omp barrier do i = 1, PEsmpTOT C1= C1 + C1S(i) end do ALPHA= RHO / C1 バリア 1 回 各自グローバルリダクション 103

最適化 (OpenMP) 1 イテレーションあたりの OMP 指示文数 (12 色の時 ) 1. Baseline 2. mvparallel1 3. nowait 4. mvparallel2 5. rmompdo 6. rmreduction 7. loopschedule 1 2 3 4 5 6 7 parallel do 40 5 5 0 0 0 0 parallel 0 3 3 0 0 0 0 do 0 35 23 28 3 0 0 do (nowait) reduction clause barrier (explicit) barrier (implicit) barrier ( 合計 ) 0 0 12 12 0 0 0 3 3 3 3 3 0 0 0 0 0 1 26 29 26 43 46 34 31 6 0 0 43 46 34 32 32 29 26 最終的に 1 回の!$omp parallel と!$omp barrier だけのプログラムになる 104

KNL 最適化結果!$omp parallel の移動 バリアフリーどちらも効果大 KNL のバリアは遅い? Baseline から 1.20 倍の性能向上 BDW 比実行効率 61.0% 70.0% Elapsed time [sec] 0.85 0.80 0.75 0.70 0.65 0.60 0.55 2 4 6 8 10 12 14 16 18 20 Color# 1 Baseline 2 mvparallel1 3 nowait 4 mvparallel 2 5 rmompdo 6 rmreduction 105

バリアフリーの効果 (KNL, KNC) Elapsed time 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 KNL # of color = 15 # of thread = 66 18.1% Elapsed time 3.0 2.5 2.0 1.5 1.0 0.5 KNC # of color = 13 # of thread = 120 27.2% 0.0 0.0 106

バリアフリーの効果 (BDW) BDW ではほとんど効果が得られない 最外に!$omp parallel を追いやることで 1.0% 性能向上 7 では 4 から 0.3% 性能低下 Elapsed time 0 ではない 3.175 BDW 3.170 3.165 3.160 3.155 3.150 3.145 3.140 3.135 3.130 3.125 3.120 # of color = 20 # of thread = 18 1.0% 107

P100 vs KNL 0.9 バンド幅比を考えると同程度 P100: 534 GB/s KNL: 490 GB/s Elapsed time [sec] 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.0 2 4 6 8 10 12 14 16 18 20 Color# P100 KNL 108

まとめ ICCG ソルバーにより P100 KNL を評価 P100 OpenACC の async 節を適切につけることが必須 CUDA は default で非同期 指示文の最適化などにより 1.21 倍の高速化 KNL バリアフリー化が効果大 一連の最適化により 1.20 倍の高速化 計算速度の向上 コアの増加による相対的な同期コスト増により バリアフリー化が重要 ただし 既存の CPU で性能低下を引き起こす可能性あり 今後の課題 MPI などの通信を含むアプリケーションでの評価 演算律速なアプリケーションでの評価 同期コストについての詳細な評価 109

Q & A 110