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

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

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

OpenACCによる並列化

OpenACC入門

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

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

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

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

OpenACC

Slide 1

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

概要 目的 CUDA Fortran の利用に関する基本的なノウハウを提供する 本チュートリアル受講後は Web 上で公開されている資料等を参照しながら独力で CUDA Fortran が利用できることが目標 対象 CUDA Fortran の利用に興味を抱いている方 前提とする知識 Fortran

Microsoft PowerPoint - GTC2012-SofTek.pptx

演習1: 演習準備

Microsoft PowerPoint - GDEP-GPG_softek_May24-2.pptx

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

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

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

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

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

PowerPoint プレゼンテーション

コードのチューニング

memo

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

01_OpenMP_osx.indd

( CUDA CUDA CUDA CUDA ( NVIDIA CUDA I

OpenMP¤òÍѤ¤¤¿ÊÂÎó·×»»¡Ê£±¡Ë

openmp1_Yaguchi_version_170530

<4D F736F F F696E74202D D F95C097F D834F E F93FC96E5284D F96E291E85F8DE391E52E >

OpenMP¤òÍѤ¤¤¿ÊÂÎó·×»»¡Ê£±¡Ë

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

OpenMPプログラミング

IPSJ SIG Technical Report Vol.2013-HPC-138 No /2/21 GPU CRS 1,a) 2,b) SpMV GPU CRS SpMV GPU NVIDIA Kepler CUDA5.0 Fermi GPU Kepler Kepler Tesla

Microsoft PowerPoint - 09.pptx

プログラミング実習I

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

Slide 1

Slide 1

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

Microsoft PowerPoint - sales2.ppt

メソッドのまとめ

(1) プログラムの開始場所はいつでも main( ) メソッドから始まる 順番に実行され add( a,b) が実行される これは メソッドを呼び出す ともいう (2)add( ) メソッドに実行が移る この際 add( ) メソッド呼び出し時の a と b の値がそれぞれ add( ) メソッド

OpenMP (1) 1, 12 1 UNIX (FUJITSU GP7000F model 900), 13 1 (COMPAQ GS320) FUJITSU VPP5000/64 1 (a) (b) 1: ( 1(a))

情報処理概論(第二日目)

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

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

インテル(R) Visual Fortran Composer XE 2013 Windows版 入門ガイド

Fortran 勉強会 第 5 回 辻野智紀

PowerPoint Presentation

gengo1-11

プログラミングI第10回

1. マシンビジョンにおける GPU の活用

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

GPU.....

kiso2-03.key

C 言語の式と文 C 言語の文 ( 関数の呼び出し ) printf("hello, n"); 式 a a+4 a++ a = 7 関数名関数の引数セミコロン 3 < a "hello" printf("hello") 関数の引数は () で囲み, 中に式を書く. 文 ( 式文 ) は

XcalableMP入門

数はファイル内のどの関数からでも参照できるので便利ではありますが 変数の衝突が起こったり ファイル内のどこで値が書き換えられたかわかりづらくなったりなどの欠点があります 複数の関数で変数を共有する時は出来るだけ引数を使うようにし グローバル変数は プログラムの全体の状態を表すものなど最低限のものに留

$ cmake --version $ make --version $ gcc --version 環境が無いあるいはバージョンが古い場合は yum などを用いて導入 最新化を行う 4. 圧縮ファイルを解凍する $ tar xzvf gromacs tar.gz 5. cmake を用

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

フローチャートの書き方

Transcription:

第 74 回お試しアカウント付き 並列プログラミング講習会 GPU プログラミング入門 in 名古屋 星野哲也 ( 助教 ) hoshino@cc.u-tokyo.ac.jp 大島聡史 ( 助教 ) ohshima@cc.u-tokyo.ac.jp 2016 年 3 月 14 日 ( 火 ) 東京大学情報基盤センター

概要 OpenACC とは OpenACC について OpenMP, CUDA との違い OpenACC の指示文 並列化領域指定指示文 (kernels/parallel) データ移動指示文 ループ指示文 OpenACC の実用例 実習 コンパイラメッセージの見方 OpenACC プログラムの実装 各種ツールの使い方 NVIDIA Visual Profiler など GPU プログラミング入門 2

OpenACC とは GPU プログラミング入門 3

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

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

OpenACC と OpenMP の実行イメージ 1 スレッド OpenMP OpenACC int main() { #pragma for(i = 0;i < N;i++) { } } CPU CPU CPU デバイス GPU プログラミング入門 6

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

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

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

想定されるハードウェア構成 ネットワーク CPU GPU ~20GB/s ~32GB/s (PCI-Express) ~200GB/s ~1,000GB/s メインメモリ (DDR など ) デバイスメモリ (GDDR など 今後は HBM など ) デバイス内外のデータ転送速度差的にも 対象とするプロセッサ内で計算が完結していることが望ましい 10

OpenACC と CUDA の違い OpenACC 指示文ベース 対象 : アクセラレータ全般 記述の自由度 高レベルな抽象化 ある程度勝手にやってくれる デバイスに特化した機能は使えない shuffle 機能を使えないなど CUDA 言語拡張 対象 :NVIDIA GPU のみ 記述の自由度 低レベルな記述 書いたようにしかやらない デバイスの持つ性能を十分に引き出せる プログラムの可搬性 可読性 ただし簡単ではない! 性能 GPU プログラミング入門 11

最低限動くプログラムを作るには 1. オフロードする領域を決める OpenACC subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc do j = 1, n do i = 1, n cc = 0 do k = 1, n cc = cc + a(i,k) * b(k,j) c(i,j) = cc end subroutine matmul attribute(global) subroutine mm_cuda(a, b, c, n) integer, value :: n real(8), dimension(n, n) :: a, b, c integer :: i, j, k real(8) :: cc i = (blockidx%x-1) * blockdim%x + threadidx%x j = (blockidx%y-1) * blockdim%y + threadidx%y cc = 0.0 do k = 1, n cc = cc + a(i, k) * b(k, j) CUDA c(i, j) = cc end subroutine mm_cuda subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc do j = 1, n do i = 1, n cc = 0 do k = 1, n cc = cc + a(i,k) * b(k,j) c(i,j) = cc end subroutine matmul 12

最低限動くプログラムを作るには 2. オフロード領域の並列化 カーネルコードの記述 OpenACC CUDA attribute(global) subroutine mm_cuda(a, b, c, n) integer, value :: n real(8), dimension(n, n) :: a, b, c integer :: i, j, k real(8) :: cc i = (blockidx%x-1) * blockdim%x + threadidx%x j = (blockidx%y-1) * blockdim%y + threadidx%y cc = 0.0 do k = 1, n cc = cc + a(i, k) * b(k, j) c(i, j) = cc end subroutine mm_cuda subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc!$acc kernels do j = 1, n do i = 1, n cc = 0 do k = 1, n cc = cc + a(i,k) * b(k,j) c(i,j) = cc!$acc end kernels end subroutine matmul OpenACC はこの時点で実行可能! subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc do j = 1, n do i = 1, n cc = 0 do k = 1, n cc = cc + a(i,k) * b(k,j) c(i,j) = cc end subroutine matmul 13

最低限動くプログラムを作るには 3. GPU 用のメモリを確保し 明示的にデータ転送する OpenACC subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc!$acc data copyin(a, b) copyout(c)!$acc kernels do j = 1, n do i = 1, n cc = 0 do k = 1, n cc = cc + a(i,k) * b(k,j) c(i,j) = cc!$acc end kernels!$acc end data end subroutine matmul CUDA attribute(global) subroutine mm_cuda(a, b, c, n) integer, value :: n real(8), dimension(n, n) :: a, b, c integer :: i, j, k real(8) :: cc i = (blockidx%x-1) * blockdim%x + threadidx%x j = (blockidx%y-1) * blockdim%y + threadidx%y cc = 0.0 do k = 1, n cc = cc + a(i, k) * b(k, j) c(i, j) = cc end subroutine mm_cuda subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc real(8), device, allocatable, dimension(:, :) :: & a_dev, b_dev, c_dev type(dim3) :: dimgrid, dimblcok allocate(a_dev(n, n), b_dev(n, n), c_dev(n, n)) a_dev(:, :) = a(:, :) b_dev(:, :) = b(:, :) dimgrid = dim3( n/16, n/16, 1) dimblock = dim3( 16, 16, 1) call mm_cuda<<<dimgrid, dimblock>>>(a, b, c, n) c(:, :) = c_dev(:, :) end subroutine matmul 14

最低限動くプログラムを作るには 4. スレッドを割り当てる OpenACC subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc!$acc data copyin(a, b) copyout(c)!$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) c(i,j) = cc!$acc end kernels!$acc end data end subroutine matmul CUDA attribute(global) subroutine mm_cuda(a, b, c, n) integer, value :: n real(8), dimension(n, n) :: a, b, c integer :: i, j, k real(8) :: cc i = (blockidx%x-1) * blockdim%x + threadidx%x j = (blockidx%y-1) * blockdim%y + threadidx%y cc = 0.0 do k = 1, n cc = cc + a(i, k) * b(k, j) c(i, j) = cc end subroutine mm_cuda subroutine matmul(a, b, c, n) real(8), dimension(n, n) :: a, b, c integer :: n integer :: i, j, k real(8) :: cc real(8), device, allocatable, dimension(:, :) :: & a_dev, b_dev, c_dev type(dim3) :: dimgrid, dimblcok allocate(a_dev(n, n), b_dev(n, n), c_dev(n, n)) a_dev(:, :) = a(:, :) b_dev(:, :) = b(:, :) dimgrid = dim3( n/16, n/16, 1) dimblock = dim3( 16, 16, 1) call mm_cuda<<<dimgrid, dimblock>>>(a, b, c, n) c(:, :) = c_dev(:, :) end subroutine matmul 15

OpenACC の指示文 GPU プログラミング入門 16

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

kernels/parallel 指示文 kernels parallel program main!$acc kernels do i = 1, N! loop body!$acc end kernels program main!$acc parallel num_gangs(n)!$acc loop gang do i = 1, N! loop body!$acc end parallel end program end program GPU プログラミング入門 18

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

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 GPU プログラミング入門 20

kernels/parallel 指示文 : 指示節 kernels 非同期実行に用いる 今回は扱わない 実行デバイス毎にパラメータを調整 if(0)/if(.false.) などとするとホスト側で実行される データの自動転送を行わないようにする データ指示文の機能を使える ( 後述 ) parallel では並列実行領域であることを意識するため 並列数や変数の扱いを決める指示節がある parallel async wait device_type if default(none) copy num_gangs num_workers vector_length reduction private firstprivate GPU プログラミング入門 21

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

データ関連指示文 アクセラレータを備えた計算機環境 MEMORY ( ホスト ) MEMORY ( デバイス ) この間のデータ移動を CPU(s) data 指示文で行う ホスト - デバイス間のデータ移動を行う データの一貫性を保つのはユーザーの責任 ホスト - デバイス間のデータ転送は相対的に遅いので要最適化 CPU GPU ~20GB/s ~200GB/s ~32GB/s (PCI-Express) ~1,000GB/s GPU プログラミング入門 メインメモリ (DDR など ) デバイスメモリ (GDDR など 今後は HBM など ) 23

データ関連指示文 :data, enter/exit data デバイス側で扱われるべきデータとその領域を指定 CUDA でいう cudamalloc, cudamemcpy, cudafree を行う data 指示文 ( 推奨 ) cudamalloc + cudamemcpy (H D) + cudafree 構造ブロックに対してのみ適用可 コードの見通しが良い enter data 指示文 cudamalloc + cudamemcpy (H D) exit data とセット 構造ブロック以外にも使える exit data 指示文 cudamemcpy (H D) + cudafree enter data とセット 構造ブロック以外にも使える GPU プログラミング入門 24

データ関連指示文 :data 指示文 Fortran subroutine copy(dis, src) real(4), dimension(:) :: dis, src!$acc data copy(src,dis)!$acc kernels do i = 1, N dis(i) = src(i)!$acc end kernels!$acc end data end subroutine copy C 言語 void copy(float *dis, float *src) { int i; #pragma acc data copy(src[0:n] dis[0:n]) { #pragam acc kernels for(i = 0;i < N;i++){ dis[i] = src[i]; } } } 構造ブロックにのみ適用可 C 言語なら {} で囲める部分 GPU プログラミング入門 25

データ関連指示文 :data 指示文イメージ Fortran ( ホスト ) ( デバイス ) subroutine copy(dis, src) real(4), dimension(:) :: dis, src!$acc data copy(src,dis)!$acc kernels do i = 1, N dis(i) = src(i)!$acc end kernels!$acc end data end subroutine copy dis, src 2dis, src の値がコピーされる 3dis _dev, src _dev の値がコピーされる dis, src 1dis, src の領域が確保される dis_dev, src_dev デバイス上の計算 dis _dev, src _dev 4dis, src の領域が解放される GPU プログラミング入門 26

データ関連指示文 :enter/exit 指示文 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); GPU プログラミング入門 27

データ関連指示文 : 指示節 data if copy copyin copyout create present present_or_... deviceptr CUDA などと組み合わせる時に利用 cudamalloc などで確保済みのデータを指定し OpenACC で扱い可とする enter data if async 非同期転送用 wait copyin create present_or_... enter data if async wait copyout delete GPU プログラミング入門 28

データ関連指示文 : 指示節 copy data 指示文へ差し掛かった時 ホスト側からデバイス側へデータをコピーし data 指示文終了時にデバイス側からホスト側へコピー copyin/copyout ホスト / デバイスからの入力 / 出力のみ行う create ただし OpenACC2.5 以降では copy, copyin, copyout の挙動は pcopy, pcopyin, pcopyout と同一 デバイス上に配列を作成 コピーは行わない present デバイス上に既に存在することを知らせる present_or_copy/copyin/copyout/create ( 省略形 :pcopy) など デバイス上に既にあれば copy/copyin/copyout/create せず なければする 29

データ関連指示文 : データ転送範囲指定 送受信するデータの範囲の指定 部分配列の送受信が可能 注意 :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 30

データ関連指示文 :update 指示文 既にデバイス上に確保済みのデータを対象とする cudamemcpy (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,:) )!$acc end data update if async wait device_type self #host と同義 host # H D device # H D GPU プログラミング入門 31

階層的並列モデルとループ指示文 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) c(i,j) = cc!$acc end kernels 32

階層的並列モデルとアーキテクチャ OpenMPは1 階層 マルチコアCPUも1 階層 CUDAは block と thread の2 階層 NVIDA GPUも2 階層 NVIDIA Kepler GPUの構成 GPU デバイスメモリ SMX 1 SMX に複数 CUDA core を搭載 各コアは SMX のリソースを共有 OpenACC は 3 階層 今後出てくる様々なアクセラレータに対応するため CUDA コア 33 33

ループ指示文 : 指示節 loop collapse gang worker vector seq auto tile device_type independent private reduction GPU プログラミング入門 34

ループ指示文 : 指示節 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.!$acc end kernels 並列化するにはループ長の短すぎるループに使う GPU プログラミング入門 35

ループ指示文 : 指示節 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 GPU プログラミング入門 36

ループ指示文 : 指示節 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!$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)!$acc end kernels OpenACC コンパイラは保守的 依存関係が生じそうなら並列化しない GPU プログラミング入門 37

ループ指示文 : 指示節 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!$acc end kernels acc reduction (+:val) 演算子対象とする変数 簡単なものであれば PGI コンパイラは自動で reduction を入れてくれる 利用できる演算子 (OpenACC2.0 仕様書より ) GPU プログラミング入門 38

関数呼び出し指示文 :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; GPU プログラミング入門 } 39

その他知っておくと便利なもの #ifdef _OPENACC OpenACC API の呼び出し時などに使う if clause の使い方 kernels/parallel, data 指示文などに使える fortran なら if(.false.), C なら if(0) を指定すると ホスト側で実行される デバッグに便利 atomic 並列領域内の atomic 領域を囲むことで利用 declare 指示文 fortran の module 内変数 C の global 変数などを使う時に用いる CUDA 関数の呼び出し CUDA 関数の呼び出しインターフェースも存在する 基本は OpenACC で作成し どうしても遅い部分だけ CUDA という実装が可能 GPU プログラミング入門 40

アプリケーションの移植方法 GPU プログラミング入門 41

アプリケーションの OpenACC 化手順 1. プロファイリングによるボトルネック部位の導出 2. ボトルネック部位のOpenACC 化 1. 並列化可能かどうかの検討 2. (OpenACCの仕様に合わせたプログラムの書き換え) 3. parallel/kernels 指示文適用 3. data 指示文によるデータ転送の最適化 4. OpenACCカーネルの最適化 1 ~ 4 を繰り返し適用 GPU プログラミング入門 42

アプリケーションの OpenACC 化手順 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 化を始める GPU プログラミング入門 43

アプリケーションの OpenACC 化手順 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 ) { } } この状態でも必ず正しい結果を得られるように作る! この時 速度は気にしない! GPU プログラミング入門 44

アプリケーションの OpenACC 化手順 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 ) { } } 徐々にデータ移動を上流に移動する GPU プログラミング入門 45

アプリケーションの OpenACC 化手順 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 ) { } } ここまで来たら ようやく個別のカーネルの最適化を始める データの転送時間が相対的に十分小さくなればいいので かならずしも最上流までやる必要はない GPU プログラミング入門 46

Q & A GPU プログラミング入門 47

実習 今回の実習の例は 全て PGI コンパイラ 16.4 を使った際の例です GPU プログラミング入門 48

実習概要 OpenACC プログラムのコンパイル PGIコンパイラのメッセージの読み方 OpenACC プログラムの作成 行列積 diffusion OpenACC プログラムの最適化 NVIDIA visual profiler の使い方など GPU プログラミング入門 49

OpenACC サンプル集 Reedbush へログイン $ ssh -Y reedbush.cc.u-tokyo.ac.jp l txxxxx module のロード $ module load pgi/17.1 $ module load cuda/8.0.44 ワークディレクトリに移動 $ cdw OpenACC_samples のコピー $ cp /home/pz0108/z30108/openacc_samples.tar.gz. $ tar zxvf OpenACC_samples.tar.gz OpenACC_samples へ移動 $ cd OpenACC_samples $ ls C/ F/ #C と Fortran 好きな方を選択 GPU プログラミング入門 50

PGI コンパイラによるメッセージの確認 コンパイラメッセージの確認は OpenACC では極めて重要 OpenMP と違い 保守的に並列化するため 本来並列化できるプログラムも並列化されないことがある 並列化すべきループが複数あるため どのループにどの粒度 (gang, worker, vector) が割り付けられたかしるため ターゲットデバイスの性質上 立ち上げるべきスレッド数が自明に決まらず スレッドがいくつ立ち上がったか知るため メッセージを見て プログラムを適宜修正する コンパイラメッセージ出力方法 コンパイラオプションに -Minfo=accel をつける GPU プログラミング入門 51

PGI コンパイラによるメッセージの確認 OpenACC_samples を利用 $ make acc_compute コンパイラメッセージ (fortran) ソースコード 8. subroutine acc_kernels() 9. double precision :: A(N,N), B(N,N) 10. double precision :: alpha = 1.0 11. integer :: i, j 12. A(:,:) = 1.0 13. B(:,:) = 0.0 14.!$acc kernels 15. do j = 1, N 16. do i = 1, N 17. B(i,j) = alpha * A(i,j) 18. 19. 20.!$acc end kernels 21. end subroutine acc_kernels pgfortran -O3 -acc -Minfo=accel -ta=tesla,cc60 -Mpreprocess acc_compute.f90 -o acc_compute acc_kernels: 14, Generating implicit copyin(a(:,:)) Generating implicit copyout(b(:,:)) 15, Loop is parallelizable 16, Loop is parallelizable Accelerator kernel generated Generating Tesla code 15,!$acc loop gang, vector(4)! blockidx%y threadidx%y 16,!$acc loop gang, vector(32)! blockidx%x threadidx%x. GPU プログラミング入門 52

PGI コンパイラによるメッセージの確認 OpenACC_samples を利用 $ make acc_compute サブルーチン名 コンパイラメッセージ (fortran) pgfortran -O3 -acc -Minfo=accel -ta=tesla,cc60 -Mpreprocess acc_compute.f90 -o acc_compute acc_kernels: 配列 aはcopyin, bはcopyoutとして扱われます 14, Generating implicit copyin(a(:,:)) Generating implicit copyout(b(:,:)) 15, Loop is parallelizable 16, Loop is parallelizable Accelerator kernel generated Generating Tesla code 15,!$acc loop gang, vector(4)! blockidx%y threadidx%y 16,!$acc loop gang, vector(32)! blockidx%x threadidx%x. ソースコード GPU プログラミング入門 8. subroutine acc_kernels() 9. double precision :: A(N,N), B(N,N) 10. double precision :: alpha = 1.0 11. integer :: i, j 12. A(:,:) = 1.0 13. B(:,:) = 0.0 14.!$acc kernels 15. do j = 1, N 16. do i = 1, N 17. B(i,j) = alpha * A(i,j) 18. 19. 20.!$acc end kernels 21. end subroutine acc_kernels 15, 16 行目の 2 重ループは (32x4) のスレッドでブロック分割して扱います 53

PGI コンパイラによるメッセージの確認 OpenACC_samples を利用 $ make acc_compute コンパイラメッセージ (C) ソースコード (C) 40. void acc_kernels(double *A, double *B){ 41. double alpha = 1.0; 42. int i,j; / * A と B 初期化 */ 50. #pragma acc kernels 51. for(j = 0;j < N;j++){ 52. for(i = 0;i < N;i++){ 53. B[i+j*N] = alpha * A[i+j*N]; 54. } 55. } 56. } pgcc -O3 -acc -Minfo=accel -ta=tesla,cc60 -Mcuda acc_compute.c -o acc_compute acc_kernels: 配列 aはcopyin, 50, Generating implicit copy(b[:1000000]) bはcopyとして扱われます Generating implicit copyin(a[:1000000]) 51, Loop carried dependence of B-> prevents parallelization Loop carried backward dependence of B-> prevents vectorization Complex loop carried dependence of B->,A-> prevents parallelization Accelerator scalar kernel generated Accelerator kernel generated ループ伝搬依存が見つかったので並列化しませ Generating Tesla code んの意 ポインタAとBが同じ領域を指していること 51, #pragma acc loop seq を警戒して 並列化しない 52, #pragma acc loop seq 52, Complex loop carried dependence of B->,A-> prevents parallelization GPU プログラミング入門 54

PGI コンパイラによるメッセージの確認 OpenACC_samples を利用 $ make acc_compute コンパイラメッセージ (C) ソースコード (C) 59. void acc_kernels(double *restrict A, double *restrict B){ 60. double alpha = 1.0; 61. int i,j; / * A と B 初期化 */ 69. #pragma acc kernels 70. for(j = 0;j < N;j++){ 71. for(i = 0;i < N;i++){ 72. B[i+j*N] = alpha * A[i+j*N]; 73. } 74. } 75. } acc_kernels_restrict: 69, Generating implicit copy(b[:1000000]) Generating implicit copyin(a[:1000000]) 70, Loop carried dependence of B-> prevents parallelization Loop carried backward dependence of B-> prevents vectorization 71, Loop is parallelizable 2 次元配列を1 次元化して扱っているため i+j*n Accelerator kernel generated Generating Tesla code が実は同じ場所を指す可能性を考慮し 70 行 70, #pragma acc loop seq 目を並列化していない 71, #pragma acc loop gang, vector(128) /* blockidx.x threadidx.x */ GPU プログラミング入門 55

PGI コンパイラによるメッセージの確認 OpenACC_samples を利用 $ make acc_compute コンパイラメッセージ (C) ソースコード (C) 78. void acc_kernels(double *restrict A, double *restrict B){ 79. double alpha = 1.0; 80. int i,j; / * A と B 初期化 */ 88. #pragma acc kernels 89. #pragma acc loop independent 90. for(j = 0;j < N;j++){ 91. #pragma acc loop independent 92. for(i = 0;i < N;i++){ 93. B[i+j*N] = alpha * A[i+j*N]; 94. } 95. } 96. } acc_kernels_independent: 88, Generating implicit copy(b[:1000000]) Generating implicit copyin(a[:1000000]) 90, Loop is parallelizable 92, Loop is parallelizable Accelerator kernel generated Generating Tesla code ようやくまともに並列化 90, #pragma acc loop gang, vector(4) /* blockidx.y threadidx.y */ 92, #pragma acc loop gang, vector(32) /* blockidx.x threadidx.x */ GPU プログラミング入門 56

実習 1 コンパイラのメッセージを確認しよう acc_compute.f90 or acc_compute.c のソースと コンパイルメッセージを見比べてください コンパイル : $ make acc_compute 注目点 parallel / kernels での違い 間接参照があった際のコンパイラメッセージ acc_kernels_bad_indirect_reference と acc_kernels_indirect_reference の比較 GPU プログラミング入門 57

PGI_ACC_TIME による OpenACC 実行の確認 PGI 環境の場合 OpenACC プログラムが実行されているかを確認するには 環境変数 PGI_ACC_TIME を使うのが簡単 使い方 ( 一般的な Linux 環境 またはインタラクティブジョブ実行時 ) $ export PGI_ACC_TIME=1 $ ( プログラムの実行 ) 一般的なスパコン環境では ジョブの中で環境変数を設定する必要が有る ジョブスクリプト中に書いてある GPU プログラミング入門 58

PGI_ACC_TIME による OpenACC 実行の確認 OpenACC_samples を利用 $ qsub acc_compute.sh 実行が終わると以下ができる acc_compute.sh.exxxxx ( 標準エラー出力 ) acc_compute.sh.oxxxxx ( 標準出力 ) $ less acc_compute.sh.exxxxx 40. void acc_kernels(double *A, double *B){ 41. double alpha = 1.0; 42. int i,j; / * A と B 初期化 */ 50. #pragma acc kernels 51. for(j = 0;j < N;j++){ 52. for(i = 0;i < N;i++){ 53. B[i+j*N] = alpha * A[i+j*N]; 54. } 55. } 56. } PGI_ACC_TIME による出力メッセージ Accelerator Kernel Timing data /lustre/pz0108/z30108/openacc_samples/c/acc_compute.c acc_kernels NVIDIA devicenum=0 time(us): 149,101 50: compute region reached 1 time 51: kernel launched 1 time grid: [1] block: [1] 起動したスレッド数 device time(us): total=140,552 max=140,552 min=140,552 avg=140,552 elapsed time(us): total=140,611 max=140,611 min=140,611 avg=140,611 50: data region reached 2 times 50: data copyin transfers: 2 device time(us): total=3,742 max=3,052 min=690 avg=1,871 56: data copyout transfers: 1 device time(us): total=4,807 max=4,807 min=4,807 avg=4,807 GPUプログラミング入門 カーネル実行時間 データ移動の回数 時間 59

実習 2 OpenACC プログラムを実行してみよう acc_data.f90 or acc_data.c のソースと コンパイルメッセージを見比べてください コンパイル : $ make acc_data プログラムを実行し PGI_ACC_TIME の出力を確認してください 実行 $ qsub acc_data.sh バッチジョブ実行が終了すると acc_data.sh.oxxxxxx ( 標準出力 ), acc_data.sh.exxxxxx ( 標準エラー出力 ) の 2 ファイルが出来る PGI_ACC_TIME の出力確認 注目点 $ less acc_data.sh.exxxxxx # 標準エラーの方 acc_data_copy, acc_data_copyinout の実行時間の違い GPU プログラミング入門 60

実習 3 OpenACC プログラムを作ろう 行列積の OpenACC 化 matmul.f90 または matmul.c を用いる acc_matmul ルーチンに OpenACC 指示文を加えてください 注意 : コンパイラの出力をよく見てください よく見るエラー例 (C): Accelerator restriction: size of the GPU copy of C,B is unknown 出力例 ====== OpenACC matmul program ====== 1024 * 1024 matrix check result...ok elapsed time[sec] : 0.00708 FLOPS[GFlops] : 302.85417 GPU プログラミング入門 61

実習 4 OpenACC プログラムを速くしよう 拡散方程式のプログラムの高速化 diffusion.f90 または diffusion.c を用いる 既に OpenACC 化されていますが 実行してみると 出力例 (C) 確認手順 (nx, ny, nz) = (128, 128, 128) elapsed time : 574.247 (s) flops : 0.078 (GFlops) throughput : 0.096 (GB/s) accuracy : 4.443942e-06 count : 1638 すごく遅い ( 特に C) 1. きちんと並列化されているか? コンパイラメッセージ 2. 無駄にデータ転送してないか? PGI_ACC_TIME 3. どっちも OK だけどなお遅い? NVIDIA Visual Profiler (nvvp) https://reedbush-www.cc.u-tokyo.ac.jp/session_login.cgi に詳細資料あり GPU プログラミング入門 62