お試しアカウント付き
並列プログラミング講習会

MPI+OpenMPで並列化されたFortranプログラムのGPUへの移行手法
東京大学 情報基盤センター
担当：星野哲也
hoshino@cc.u-tokyo.ac.jp（内容に関するご質問はこちらまで）
1

講習会スケジュール
n 開催日時ü 12月7日（水） 13:00 – 17:00

n プログラム
13:00 – 13:50

OpenACC, OpenMP 5.x, do concurrent, CUDA
Fortran の特徴

14:00 – 14:50

OpenMP for CPUからOpenACC, OpenMP 5.x, doconcurrentへの移植⼿法

15:00 – 15:50

演習１︓簡単なサンプル

16:00 – 16:50

演習２︓有限要素法

2

講習会についてn 本講習会はü OFP-IIに向け、MPI+OpenMPで記述されたFortranプログラムのGPU移植の手法を中心に扱います。
n その他の講習会

https://www.cc.u-tokyo.ac.jp/events/lectures/n スパコンイベント情報メール配信サービス

Youtubeにて過去の講習会を配信中！
https://www.youtube.com/channel/UC
2CHaGp1AO-vqRlV7wmU0w/videos?view=0&sort=p&flow=grid

https://regist.cc.u-tokyo.ac.jp/announce/
ü 講習会や研究会の案内、トライアルユースの実施のお知らせなどを配信しています。
3

講習会の進め方
n Zoomを利用したオンライン講習会ですü この講義は録画されていますü 質問があるとき以外はミュートでお願いしますü ビデオもオフを推奨します

n slackを使って質問に対応しますü slackはリンクを知っている人は誰でも使える設定になっていますü slackのリンクをzoomのチャットに貼るので、未登録の場合は今のうちに登録お願いしますü スクリーンショットなどで画像を共有することで、質問対応しますü Windows：Alt + PrtScn で作業中ウィンドウのスクショがクリップボードにコピーされます。

slackのチャット部分で貼り付け(Ctrl + V)することで画像をアップロードできますü Mac : command + shift + control + 4 の同時押し、その後撮りたいウィンドウ上でspaceを押す
ことで、スクリーンショットがクリップボードにコピーされます。slackのチャット部分で貼り付け(command + V)することで画像をアップロードできます4Zoom: 「手を挙げる」方法
1.

Zoomメニュー中の「リアクション」をクリック

2.

ポップアップで表示された「手を挙げる」をクリック

5

Zoom: 手が挙がっていることの確認方法
1.

Zoomメニュー中の「参加者」をクリックして，参加者一覧を表
示2.

表示された参加者一覧の，自分のところを見ると手が挙がっている6

Zoom: 「手を降ろす」方法
1.

Zoomメニュー中の「リアクション」をクリック

2.

ポップアップで表示された「手を降ろす」をクリック

7

Slack: 質疑応答チャンネルへの移動
n 左側のメニューバーのチャンネル一覧内に「第196回-fortran2gpu」があるので，クリック
n 表示されていない場合
1.
2.
3.

「チャンネルを追加する」をクリック「チャンネル一覧を確認する」をクリック「第196回-fortran2gpu」があるので，「参加する」をクリック

Slack: メッセージの入力方法
n 最下部に入力欄があるので，質問内容を記載して Ctrl+Entern 入力後に右下の「メッセージを送信する」をクリックしても同じ（メッセージ入力前には，「メッセージを送信する」は押せない）
メッセージの⼊⼒欄
コードブロックの⽣成

メッセージを送信する

n コードを入力する際には，「コードブロック」がおすすめ
n 枠が生成されるので，この中にコピペするのが簡単かつ見やすいn ```（JIS配列ならばShift+@を3連打）しても枠が生成される9

東大情報基盤センターのスパコン概要10

2001-2005
Hitachi SR8000
1,024 GF

Hitachi
SR2201307.2GF

2006-2010
Hitachi SR11000
J1, J25.35 TF, 18.8 TF

2011-20152016-20202026-2030

Hitachi SR16K/M1
Yayoi
54.9 TF

Hitachi
SR8000/MPP

OBCX(Fujitsu)

2,073.6 GF

6.61 PF

OakforestPACS (Fujitsu)

Hitachi HA8000
T2K Todai
Fujitsu FX10
Oakleaf-FX

東京大学情報基盤センターのスパコン

OFP-II

100+ PF

25.0 PF

140 TF

利用者2,600+名
55％は学外2021-2025

Fujitsu
33.1 PF

1.13 PFBDEC02250+ PF

ReedbushU/H/L (SGI-HPE)
3.36 PF

Ipomoea-01 25PB

Ipomoea03

Ipomoea-02
11

2001-2005
Hitachi SR8000
1,024 GF

SR80002006-2010
Hitachi SR11000
J1, J2
5.35
18.8 TF
IBMTF,
Power5+

Hitachi
SR2201

Hitachi
SR8000/MPP

HARP-1E

SR8000307.2GF

2011-20152016-2020

Hitachi SR16K/M1
Yayoi
54.9
TF
IBM
Power7

6.61 PF

OakforestPACS (Fujitsu)

Hitachi HA8000
T2K Todai
140 TFAMD Opteron

汎用CPU
加速装置付

2026-2030

Intel CLX
OBCX(Fujitsu)

2,073.6 GF

疑似ベクトル2021-2025

OFP-II

100+ PF

25.0 PF

AcceleratorsIntel Xeon Phi
Fujitsu FX10
Oakleaf-FX

SPACR64 IXfx

BDEC02

Fujitsu
33.1 PF

1.13 PF

ReedbushU/H/L (SGI-HPE)

A64FX,
Intel Icelake+
NVIDIA A100

250+ PF

Accelerators

3.36 PFIntel BDW +NVIDIA P100

Ipomoea-01 25PB

Ipomoea03

Ipomoea-02
12Platform for Integration of (S+D+L)
Big Data & Extreme Computing

Reedbush (HPE, Intel BDW + NVIDIA P100 (Pascal))

Simulation Nodes:
Odyssey
Fujitsu/Arm A64FX
25.9PF, 7.8 PB/s

Shared File
System
（SFS）
25.8 PB, 500 GB/s

2.0 TB/s

Data/Learning
Nodes: AquariusIntel Ice Lake + NVIDIA A100

Fast File
System（FFS）
1 PB, 1.0 TB/s

7.20 PF, 578.2 TB/s

Oakforest-PACS (OFP) (Fujitsu, Intel Xeon Phi (KNL))• JCAHPC （筑波大CCS・東大ITC），2016年10月～2022年3月末（予定）• 25 PF, #39 in 58th TOP 500 (November 2021)

Oakbridge-CX (OBCX) (Fujitsu, Intel Xeon CLX)

800 Gbps

External
Resources

External
Resources
External Network

Simulation Nodes（Odyssey）• データ解析・シミュレーション融合スーパーコンピュータ• 2016年7月～2021年11月末（引退）• 東大ITC初のGPUクラスタ，ピーク性能3.36 PF

Data/Learning Nodes（Aquarius）• 2019年7月～2023年6月末（予定）• 6.61 PF, #110 in 58th TOP500-June 2023 (Plan)

Wisteria/BDEC-01（Fujitsu）• シミュレーションノード群（Odyssey）：A64FX (#17)• データ・学習ノード群（Aquarius）：Intel Icelake+NVIDIA A100) (#106)• 33.1 PF, #13 in 57th TOP 500，2021年5月14日運用開始• 「計算・データ・学習（S+D+L）」融合のためのプラットフォーム• 革新的ソフトウェア基盤「h3-Open-BDEC」（科研費基盤（S） 2019年度～2023年度）

Reedbush

Oakforest-PACS

Oakbridge-CX 13

MPI+OPENMPからのGPUへの移行手法

14

MPI + “X”（Fortran向け）
n 従来型のスパコン
n MPI + OpenMP for CPU (どこのスパコンでもほぼ使える）

n GPUスパコン
n MPI + OpenACC (NVIDIA)n MPI + OpenMP for GPU (NVIDIA, AMD, Intel)
n MPI + do concurrent (NVIDIA)n MPI + CUDA Fortran (NVIDIA)

従来型のスパコンではほとんどの場合”X=OpenMP”だったが、GPUスパコン
では”X”の選択肢が複数ある状態
15

GPUプログラムの実行イメージ
1スレッド

OpenMP
for CPU

OpenACC, OpenMP for GPU,
Do concurrent, CUDA

int main(){...
#このループを並列実行
for (i=0; i<n; i++) {
...
}
...
}

CPU

CPU

CPU

デバイス
(GPU)
16

GPUプログラミングの必須要素
1.

CPU-GPU間データ移動
n CPUとGPUで独立のメモリを持つため
n メモリ管理方針は“X”により異なる

バス (PCIe など)

CPU
OSが動いているGPU~40GB/s
相対的に遅い~1,600GB/s~200GB/sn CPU-GPU 変数独立型
n 変数同一視型
n Unified memory型（メモリアドレス空間

ごと同一視）

2.

ループ並列化
n いずれの”X”でも、プログラム中のループ構造を並列化することでGPUで高速実行するn “X”によって並列化できるループ、できないループがある

デバイスメ
モリ

メインメモリ

物理的に独立
世の中のすべてのループ

GPUで(≒CUDAで)高速化可能なループ
OpenACC, OpenMP for GPUで高速化可能なループ
Do concurrentで高速化可能なループ17

仕様の比較 (nvidia compiler 22.5 の場合)
OpenACC
kernels

OpenMP 4.x
target teams distributeparallel do

OpenMP 5.x
target loop

Fortrando concurrent

CUDA Fortran

変数同一視
Unified Memory ※1

変数同一視
Unified Memory ※1

変数同一視
Unified Memory ※1

Unified Memory

変数独立関数呼び出し✓✓✓N/A ※2✓

リダクション

✓

✓

✓

✓ ※3

✓

atomic演算

✓

✓

✓

N/A

✓

スレッド数の調整

✓ ※4

✓ ※4

N/A

N/A

✓

Streamの非同期
実行✓

N/A ※5

N/A ※5

N/A✓

GPU組み込み関数

N/A

N/A

N/A

N/A

✓

単一ソースでのCPU・GPU両対応

✓

△

✓

✓

N/A

CPU-GPUメモリ管理※１ Unified memoryはNVIDIA GPU, compilerの機能であり、OpenACC, OpenMPの仕様準拠ではない。
※２ Fortranの仕様上は、pure functionであれば呼び出せるはずだが、pure function呼び出しもサポートされていない。
※３ NVIDIA compilerの独自拡張であり、Fortran do concurrentの仕様準拠ではない。
※４ CUDA程自由ではない。32以上の２の冪乗数以外を指定しても無視されることが多い。OpenACCではkernelsではなくparallelを使うと自由度が少し上がる。
18※５ omp task指示文と組み合わせると書けそうな気もするが、うまくいかない。

CPU-GPUのメモリ管理
n

CPU-GPU変数独立型 (CUDA)
配列ごとのPinned memoryの設定など、細やかな指定ができる
n Pinned memory: ページロックメモリ。CPU-GPU間のデータ転送速度が速い
n CPU-GPUの変数の一貫性をプログラマが明示的に管理する必要があり、管理コストが大きいnn

変数同一視型 (OpenACC, OpenMP)
CPU-GPUの変数の区別がプログラムの見かけ上はなくなるので、コードの見通しがいいが、CPU-GPU間の一貫性の管理は依然プログラマの仕事
n 配列ごとのPinned memoryの設定など、細かな指定はできない
nn NVIDIAコンパイラではオプションによる一括指定はできる

nUnified Memory型 (do concurrent, OpenACC, OpenMP)nCPU-GPU間の一貫性の管理から解放されるため、圧倒的に楽n CPU-GPU間のデータ転送を書く必要がない
n ポインタを含む構造体などもそのまま使えるn

速度的に不利n ページ単位（ページサイズの設定不可、サイズ非公開）で転送するため
n GPU Direct RDMA通信が使えない

nCPU-GPU間通信がバックグラウンドで実行されるため、高速化を目指す上でわかりづらい

19CPU-GPU間データ転送の書き方
Real(8), allocatable :: X_cpu(:)
Real(8), allocatable, device :: X_gpu(:)
allocate(X_cpu(N))
allocate(X_gpu(N))
X_cpu(:) = 0
X_gpu(:) = X_cpu(:)
Call cuda_func<<<tb,th>>(X_gpu)
X_cpu(:) = X_gpu(:)

CPUメモリ
0x01

n Unified Memoryn 変数同一視型

n 変数独立型

GPUメモリ

X_cpu

Real(8), allocatable :: X(:)
allocate(X(N))X(:) = 0!$acc data copy(X)Call openacc_func(X)!$acc end data

Real(8), allocatable :: X(:)
allocate(X(N))X(:) = 0
Call do_concurrent_func(X)(X_cpu, X_gpu)のペアをソフトウェア的に変数Xとして同一視する

CPUメモリ
0x01

GPUメモリ

メモリアドレス空間を一致させることで、
X_cpu, X_gpuの区別を無くす
データ転送はページフォルトが起きた際にバックグラウンドで行われる

CPUメモリ

X
0x10

0x11

X_gpu

GPUメモリ

0x11

X

0x10

X

X
20

CPU-GPU間のデータ移動
OpenACC data指示文を使った場合

program main
implicit none
! 変数宣言allocate(a(n),b(n))
c = 2.0
do i = 1, na(i) = 10.0
end do!$acc data copyin(a) copyout(b)!$acc kernels!$acc loop independent
do i = 1, nb(i) = a(i) + c
end do!$acc end kernels!$acc end data
sum = 0.d0
do i = 1, n
sum = sum + b(i)end doprint *, sum/ndeallocate(a,b)
end program main

CPU

GPU
GPUのメモリ上にコピーを作る。
これは変数独立型、変数同一視型、Unified
Memory型のいずれの場合でも同じ。

a b

メモリ確保

GPUへ
copyin a

b

メモリ確保

カーネル
実行

解放
GPUから
copyout21

“X”に何を選ぶべきか（私見）n OpenACC（推奨）n NVIDIAのGPUで最も使い勝手が良く、コンパイラが成熟している
n AMD, Intelがサポートすることはおそらくない
n CUDA Fortranとも組み合わせやすいn OpenMP for GPU
n NVIDIA, AMD, Intel全てがサポートするということが最大のメリット

n OpenACCと仕様上の差はそれほど大きく無いが、コンパイラが成熟していない

n NVIDIAコンパイラはOpenMP 5.xのloop指示文メインだが、AMDは対応していない。OpenMP

4.xのdistribute~指示文はNVIDIAで性能が出ないなど

n AMDのFortranサポートは特に遅れており、Fortranを使うなら事実上NVIDIAしか選択肢がない

n IntelはGPUの開発自体が遅れている

n Do concurrent（Fortranの言語標準）
n 言語標準であるということが最大のメリット
n 仕様上の制約が非常に大きく、実アプリで使うならそれなりに書き換える必要がある
n 書き換えを進めていき、do concurrentではどうしようもなくなって、OpenACCやCUDAを混ぜた瞬間、言語標準というメリットを失う

22

データ転送方式は？（私見）n OpenACC
n ひとまずはUnified Memoryから始めることを推奨
n ループの並列化に集中できる
n 速度的な問題が出てきたら、データ指示文を追加すれば良い
n OpenMP for GPUn ひとまずはUnified Memoryから始めることを推奨
n メリットはOpenACCの場合と同じ
n Unified MemoryはOpenMPやOpenACCの仕様に組み込まれているわけではない
ので、NVIDIA以外で動くかどうかはわからない
n OpenACCはNVIDIAしかサポートしていない（GCCとかもサポートしていることになっているが使い物にならない）ので、その点はデメリットにならない

n Do concurrent
n Unified memoryしか選択肢がないため、速度的な問題が起きても解消できない

23

CUDAの立ち位置は？（私見）n ライブラリ的な利用方法を推奨
n 単純なループにおいて、CUDA以外との性能差はほぼない
n 指示文などではどうしようもない複雑なループや、少しでも速くしたい

アプリケーションのメインのボトルネック部分などは、CUDAの利用を考えるべき
n いずれの”X”でもCUDA Fortranと組み合わせられる

n 少なくとも、アプリケーション全体をCUDAに置き換える積極的なモチベーションはもはや無い
n AMD, Intelは？
n AMD: hipと呼ばれるほぼCUDA C互換のものがある。よく出来ているが、

Fortran対応はない。Cメインのユーザであるなら、AMDは現時点でも強力な選択肢
n Intel: DPC++なるものを推している。Fortran対応はない

24

ループ並列化の例：一重ループ
OpenMP for CPU

OpenACC

OpenMP 4.x
teams distribute parallel do

OpenMP 5.x
target loop

Fortran do concurrent

基本的に、このループは並列化可能ですよと宣言しているだけ。CUDA含め比較しても、性能はほぼ変わらない。

25

ループ並列化の例：多重ループ
OpenMP for CPU

OpenMP 4.x
teams distribute parallel do

OpenACC

Fortran do concurrent

OpenMP 5.x
target loop

単純なループ構造であれば、基本的には各ループの並列可能性を示せばいい。
多重ループでは階層的な並列性を意識する必要性が出てくる。（特にOpenMP 4.x）26

ループ並列化の例：多重ループの一重化
OpenMP for CPU

OpenACC

OpenMP 4.x
teams distribute parallel do

OpenMP 5.x
target loop

Fortran do concurrent

Do concurrentでこのように書いた時、仕様上ループの順序についての決めが無いため、どのループを最内として扱うかはコンパイラが決めることにな
るCollapse節によって一重化できるな
ら、した方が良いかもしれない。
特にOpenMP 4.xでは並列性を確保するためにcollapseが重要。

27

GPUの階層構造 (1/2)
NVIDIA A100 Tensor コア GPU アーキテクチャの詳細

n A100は108個のSM (Streaming Multiprocessor)を持つ

NVIDIA
Tensor
図4.
128 個の SM を搭載した GA100 フル GPU (A100 出典:
Tensor
コアA100GPU
は core GPUアーキテクチャ
108 個の SM を搭載)

28

GPUの階層構造 (2/2)
n L2キャッシュはSM全体で共有
n L3キャッシュはないので、L2がラスト

レベルn L1キャッシュはSM毎に独立
n Shared memoryと共有で192 KB
n キャッシュブロッキングを考えるなら

L1キャッシュ単位n 同期ができるのもSMの内部のみ
n 限定的な条件下で全体同期もできるが、あまり使わないn SM内のスレッドは更にWarpと呼ばれる32スレッド単位で実行される

図 出典:
5.
GA100 ストリーミング
マルチプロセッサ(SM)
NVIDIA
A100 Tensor
core GPUアーキテクチャ

29GPUプログラミングの階層構造
n CUDA, OpenACC, OpenMP for GPU も、GPUの階層構造に合わせて、階層的な構造を持つ

スレッドブロック（CUDA）
gang（OpenACC）
team（OpenMP）

n CUDA: スレッドブロック、スレッド
n OpenACC: gang, (worker, ) vector
n OpenMP: team, threadn 一つのスレッドブロック/gang/team

スレッド（CUDA）
vector（OpenACC）
スレッド（OpenMP）に所属するスレッドは同じSMに割り当てられる
n つまりL1キャッシュが共有される
n 1ブロックあたりのスレッド数は1〜1024

n Warpを踏まえると、32の倍数、経験的に64, 128, 256あたりがいい
n CPU-GPU間のデータ移動や、リソースを使い切ることの方が重要なので、最初からそこまで意識する必要はない

cited from : http://cudaprogramming.blogspot.jp/2012/12/thread-hierarchy-in-cudaprogramming.html

30

行列積
n 計算律速なカーネル
n キャッシュブロッキングが効果的
n 複雑なループ構造に対応できるのか？
do j = 1, n
do i = 1, n
do k = 1, n
C(i,j) = C(i,j) + A(i,k) * B(k,j)
end doend doend do

実装１:ナイーブ三重ループ

do j = 1, n, BLK
do i = 1, n, BLK
do k = 1, n, BLK
do jj = 0, BLK-1
do ii = 0, BLK-1
do kk = 0, BLK-1
C(i+ii,j+jj) = C(i+ii,j+jj)&+ A(i+ii,k+kk) * B(k+kk,j+jj)
end doend doend doend doend doend do

実装２:ブロッキング六重ループ

do j = 1, n, BLK
do i = 1, n, BLK
do jj = 0, BLK-1
do ii = 0, BLK-1
cc1(ii,jj) = 0.0
end doend dodo k = 1, n, BLK
do jj = 0, BLK-1
do ii = 0, BLK-1
ccc = 0.0d0
do kk = 0, BLK-1
ccc = ccc + A(i+ii,k+kk) * B(k+kk,j+jj)
end docc1(ii,jj) = cc1(ii,jj) + ccc
end doend doend dodo jj = 0, BLK-1
do ii = 0, BLK-1
C(i+ii,j+jj) = cc1(ii,jj)
enddo
enddo
end doend do

実装３:ブロッキングwithローカル変数 31

行列積
2500
実装1：ナイーブ

実装2：ブロッキング

実装3：ローカル変数

500

Uns
u

!
ppo
rted

Erro
r!

1000

Uns
u

ppo
rted

!

GFlops

1500

遅す
ぎて
見え
ない
Uns
upp
orte
d!

2000

0
NVIDIA A100
OpenACCNVIDIA A100
OpenMP target
teams distributeparallel do

NVIDIA A100
OpenMP target
loop

NVIDIA A100
Fortran do
concurrent

AMD MI100
OpenACC

AMD MI100
OpenMP target
teams distributeparallel do

AMD MI100
OpenMP target
loop

AMD MI100
Fortran do
concurrent※A100 コンパイルコマンド： nvfortran (version 22.5) (-stdpar=gpu –acc=gpu –mp=gpu) –Minfo=accel,mp –gpu=cc80,managed※MI100 コンパイルコマンド： amdflang (version 14.0, roc-5.2.0) -target x86_64-pc-linux-gnu -fopenmp -fopenmp-targets=amdgcn-amd32
amdhsa -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx908 -O3

ループ並列化の例：リダクションループ
OpenMP for CPU

OpenACC

OpenMP for GPU

Fortran do concurrent

OpenACC, OpenMP for GPU のリダ
クションの実装はOpenMP for CPUと大差ない

NVIDIAコンパイラでは、リダクションを勝手に判定してくれるが、Fortranの仕様上はまだリダクションはサポートされていない。
そのため上記コードが将来的に動くかどうかはわからない。
33

ループ並列化の例：atomic演算
OpenMP for CPU

OpenACC

OpenMP for GPU

Fortran do concurrent

Atomic計算は、OpenMPでもOpenACCでも書き方はほぼ同じ。
Fortran do concurretではサポートされていない。

N/A

34

ループ並列化の例：配列代入
OpenMP for CPU

OpenMP for GPU

N/A?

OpenACC

Fortran do concurrent

使えると便利な配列代入だが、今のところOpenACCでしか使えない。
OpenMP for GPUでは、コンパイルは通るものの並列化されない。

N/A

35

ループ並列化の例：関数呼び出しを含むループ
OpenMP for CPU

あまり認知されていないが、OpenMP for
CPUでもSIMDループ内で関数呼び出しをする際にはdeclare simdが必要。

OpenMP for GPU

Declare target指示文が必要なはずだが、NVIDIAコンパイラ(22.5)では付けなくても動くし、付けるとコンパイルが途中で止まる。
原因不明。

OpenACC

呼び出される関数側と、呼び出し元の関数側にroutine指示文を書く必要がある。
36

ライブラリの呼び方
n 最も手っ取り早いのは、Unified memoryの使用の有無に関わらず、

data指示文を使う方法

n OpenACC の指示文を使う場合

n OpenMP のtarget data指示文でも可n Do concurrentでも、コンパイルオプションに-accをつけて、上記のように書くのが手っ取り早い

37

MPIとの繋ぎnUnified Memoryを使う場合
コード上で変更することは特になし
n GPUのメモリからCPUのメモリ（MPIで送受信するバッファ）への暗黙のコピーが発生するため、性能上不利になり得る
nnUnified Memoryを使わない場合
n

CUDA-aware MPIを使うのが基本
n MPI関数の引数に、GPU上のポインタを受け付けるMPIn OpenMPIからも提供されているn

指示文を使って、GPU上のポインタを直接MPI関数の引数として渡す
OpenACC: host_data指示文
n OpenMP: use_device_ptr指示節
n38

MPIランクとGPU割り当てn

OpenACC関数を呼ぶためヘッダーを追加
C: #include <openacc.h>
Fortran: use openaccnノード上のGPU数の取得(acc_get_num_devices)
const int ngpus = acc_get_num_devices(acc_device_nvidia);

acc_device_nvidiaを指定することで、NVIDIA GPUを数える。
nあるプロセスに特定のGPUを割り当て(acc_set_device_num)
int rank= 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
acc_set_device_num(rank % ngpus, acc_device_nvidia);

device_num を rank % ngpus とすることで、ノード内のプロセスは異なるGPUを利用することとなる。これを呼ばないと、0番を使いに行く。
39

拡散方程式カーネル（7点ステンシル）
n メモリ律速なカーネル
n Z軸一次元分割によりマルチGPU化
n 通信と計算のオーバーラップはできるのか

3次元拡散方程式カーネルのdo concurrent実装

40

拡散方程式カーネルのストロングスケーリング
7000
OpenACC_managed
6000

OpenACC_data
OpenACC_data_async

GFlops

5000

OpenMP_managed
OpenMP_data

4000

Fortran do concurrent

3000
2000
1000
0
1

2

4

8

16

32

GPU数

※問題サイズ 5123、倍精度、ストロングスケーリング※WisteriaのAquariusノード群を使用。1ノードあたりA100 GPUを8枚搭載※A100 コンパイルコマンド： nvfortran (version 22.5) (-stdpar=gpu –acc=gpu –mp=gpu) –Minfo=accel,mp –
gpu=cc80(,managed)※OpenMPI 4.1.4 を使用。

41

有限要素法コードのGPU移植実習

42

Target: GeoFEM/Cuben Parallel FEM Code (& Benchmarks)n 3D-Static-Elastic-Linear (Solid Mechanics)
n Performance of Parallel Preconditioned Iterative Solvers– 3D Tri-linear Hexa. Elements– SPD matrices: CG solver
U =0 @ y=Y– Fortran90+MPI+OpenMP– Distributed Data Structure(N -1) elements– Block Diagonal LU + CG
N nodes– Block CRS Format– MPI，OpenMP，OpenMP/MPI HybridyUniform Distributed Force inz-direction @ z=Zmax

min

z

z

• only OpenMP casezx

Ux=0 @ x=Xmin(Ny-1) elements
Ny nodesy
Uz=0 @ z=Zmin(Nx-1) elements
Nx nodes43

サンプルコードのダウンロード
n 講習会で使うサンプルコードは以下にあります
n /work/gt00/share/lecture_fortran2gpu.tar.gz

n ダウンロード手順1.

$ ssh tXXXXX@wisteria.cc.u-tokyo.ac.jp
n wisteriaへのssh。tXXXXXはアカウント名$ cd /work/gt00/tXXXXX
3. $ cp /work/gt00/share/lecture_fortran2gpu.tar.gz .
4. $ tar zxvf lecture_fortran2gpu.tar.gz
5. $ cd lecture_fortran2gpu/
2.

44

実習
n 有限要素法プログラムのCGソルバのGPU移行
n サンプルコード： OpenMP_for_CPU/GeoFEM/01_naive
n ステップ
1.
2.
3.
4.

CPUで実行して、参照となる結果を得ましょう
Makefileを書き換えましょう
まずはUnified Memoryを利用し、ループの並列化をしましょう
指示文を使って、データ転送を実装しましょう45

ステップ１：CPUの結果
n 実行バイナリはOpenMP_for_CPU/GeoFEM/runに生成されます
n 実行パラメータはmesh.inpで調整できます$ cat mesh.inp
128 128 128
1
1
1
1
1
2000
-8

並列化の際のカラーリング手法と色数。
今回は気にしなくて良い。

プロセスあたりの問題サイズ (NX*NY*NZ)MPI プロセス数
例えば 1 2 2 にすると、
128 * 256 * 256 の問題
を4プロセスで解くための問題になる講習会コードでは未使用。
今回は気にしなくて良い

128
128
128
1
1
1
1
-8
### NORMAL### CM-RCM
color number:
8color number:
85.239190E-02
coloring of elements
1.402784E+01
matrix assembling100
7.200837E-02
200
7.698423E-04
収束履歴（多少ブレる）300
2.321115E-05
400
3.515262E-07
457
9.940129E-09
elapsed457
1.783206E+02
1.783206E+02
1.783206E+02
iter457
3.901982E-01
3.901982E-01
3.901982E-01
2097152-3.810000E+01-3.810000E+01
1.270000E+02

CG法全体の時間(elapsed),
反復あたりの時間(iter)

収束後のある要素の値。
1283 の時はこの値になる。
46

ステップ２：Makefileの書き換え
n 書き換えの前に、workディレクトリを作ります
n $ cd OpenMP_for_CPU/GeoFEM/
n $ cp –r 01_naive 00_work
n $ cd 00_work
F90
F90OPTLOPTTARGET= mpifort= -Mpreprocess -fast -mp= -Mpreprocess -fast -mp= ../run/01_naive
OpenACCの場合

F90
F90OPTLOPTTARGET= mpifort= -Mpreprocess -fast -acc -gpu=managed -Minfo=acc= -Mpreprocess -fast -acc -gpu=managed -Minfo=acc= ../run/00_work47

ステップ３：ループの並列化
n まずは solver_CG_3_SMP_novec.f を書き換えますn OpenMP 指示文のついたループを、OpenACCやOpenMP for GPUなどに書き換えてくださいn この時、結果に変動がないかどうかを実行して確認しながら進めると良いです48

ステップ４：指示文によるデータ移動
n Makefileを編集し、managedを削除します
F90
F90OPTLOPTTARGET= mpifort= -Mpreprocess -fast -acc -gpu=managed -Minfo=acc= -Mpreprocess -fast -acc -gpu=managed -Minfo=acc= ../run/00_workn このコードはgo to文が含まれるため、通常のdata指示文が使えません。代わりにenter data, exit data指示文を使います
n !$acc enter data copyin(a) create(b)n !$acc exit data delete(a) copyout(b)49