219
長岡技術科学大学 電気電子情報工学専攻 出川智啓 GPGPU講習会補足資料 GPGPUCUDA Fortran

GPGPU Seminar (GPGPU and CUDA Fortran)

  • Upload
    -

  • View
    409

  • Download
    4

Embed Size (px)

Citation preview

Page 1: GPGPU Seminar (GPGPU and CUDA Fortran)

長岡技術科学大学 電気電子情報工学専攻 出川智啓

GPGPU講習会補足資料GPGPUとCUDA Fortran

Page 2: GPGPU Seminar (GPGPU and CUDA Fortran)

長岡技術科学大学 電気電子情報工学専攻 出川智啓

GPUのアーキテクチャ

Page 3: GPGPU Seminar (GPGPU and CUDA Fortran)

GPU(Graphics Processing Unit)とは

画像処理専用のハードウェア

具体的には画像処理用のチップ

チップ単体では販売されていない

PCI‐Exカードで販売

チップ単体と区別せずにGPUと呼ぶことも多い

ノートPCに搭載

PCI‐Exカードとして販売されるGPUには,ビデオメモリと呼ばれるDRAMが搭載

GPGPU講習会3 2016/01/13

Page 4: GPGPU Seminar (GPGPU and CUDA Fortran)

GPU(Graphics Processing Unit)とは

GPGPU講習会4

代表的な製品

NVIDIA GeForce AMD Radeon Intel HD Graphics(内蔵)

代表的な用途

3Dグラフィックス処理

3Dゲーム,3DCAD,3DCG作成

エンコード・デコード支援

GPU上に専用チップを搭載していることが多い

デスクトップPCのGUI処理

Windows Aeroが比較的高性能なGPUを要求

2016/01/13

Page 5: GPGPU Seminar (GPGPU and CUDA Fortran)

GPU(Graphics Processing Unit)の役割

グラフィックスを表示するために様々な処理を行い,処理の結果をディスプレイに出力

3次元グラフィックスの発展に伴って役割が大きく変化

3次元座標変換

ポリゴンとピクセルの対応付け

ピクセル色計算テクスチャ参照

フレームバッファ(ビデオメモリ)への書き込み

ディスプレイ出力

CPU

ディスプレイコントローラ GPU

3次元座標変換

ポリゴンとピクセルの対応付け

ピクセル色計算テクスチャ参照

フレームバッファ(ビデオメモリ)への書き込み

ディスプレイ出力

現在過去

CPUが3D描画の演算を実行

GPUが出力

描画情報

画面出力

GPUが演算から出力までの全てを担当

CPUは描画情報の生成やGPUへの情報の引き渡し,GPUの制御を行う

描画情報

画面出力

GPGPU講習会5 2016/01/13

Page 6: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの描画の流れ

1. CPUからGPUへ描画情報を送信

2. 頂点処理(頂点シェーダ) 座標変換

画面上での頂点やポリゴンの位置・大きさの決定

頂点単位での照明の計算

3. 頂点やポリゴンからピクセルを生成(ラスタライザ)

4. ピクセル処理(ピクセルシェーダ) 画面上のピクセルの色

テクスチャの模様

5. 画面出力 ピクセルの色情報をフレームバッファに書き込み

2.

3.

4.

GPGPU講習会6 2016/01/13

Page 7: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードの利点

CPUで描画のための演算を行うと,CPUにかかる負荷が大きい

3次元画像処理の専用回路を備えたハードウェアを導入

○CPUにかかる負荷を減らすことができる

○頂点・ピクセルごとに並列処理が可能なため,ハードウェアによる並列処理が可能

GPGPU講習会7 2016/01/13

Page 8: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードの欠点

3次元画像処理の専用回路を備えたハードウェアを導入

×新しい描画方法を開発しても,GPUへ実装・製品化されるまで利用できない

×ユーザが所有しているGPUによって利用できる機能にばらつきが生じる

×ある描画手法用の専用回路を実装しても,その描画方法が常に使われる訳ではないのでGPU全体の利用効率が下がる

GPGPU講習会8 2016/01/13

Page 9: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードからGPUへ

CGの多様化と共に固定機能の実装が困難に

頂点処理とピクセル処理をユーザが書き換えられるプログラマブルシェーダの実装

頂点処理用回路

ピクセル処理用回路

ビデオカード

頂点シェーダユニット

ピクセルシェーダユニット

GPU

GPGPU講習会9 2016/01/13

Page 10: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードからGPUへ

描画する画像によって頂点処理とピクセル処理の負荷が変化

処理によっては利用効率に差が発生し,利用効率が低下

GPU

頂点シェーダユニット

ピクセルシェーダユニット

頂点処理重視の処理

GPU

頂点シェーダユニット

ピクセルシェーダユニット

ピクセル処理重視の処理

空きユニット

空きユニット

GPGPU講習会10 2016/01/13

Page 11: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードからGPUへ

頂点シェーダとピクセルシェーダを統合したユニファイドシェーダへの進化

頂点処理とピクセル処理を切り替えることで利用率を向上

GPU

ユニファイドシェーダユニット

頂点処理重視の処理 ピクセル処理重視の処理

GPU

ユニファイドシェーダユニット

GPGPU講習会11 2016/01/13

Page 12: GPGPU Seminar (GPGPU and CUDA Fortran)

ビデオカードからGPUへ

各ピクセルに対して並列に処理実行できるように進化

単純な処理を行う演算器を大量に搭載

高い並列度で処理を実行

GPUの誕生とGPGPUの普及

高性能な3DCG画像処理への要求→GPUの高性能化

GPUの長所

消費電力あたりの浮動小数点理論演算性能が高い

GPU単体の消費電力は高い

(相対的に)安価

CPUだけで同等の計算能力を達成するより安価

GPGPU講習会12 2016/01/13

Page 13: GPGPU Seminar (GPGPU and CUDA Fortran)

Teslaアーキテクチャ*

GPGPU講習会

Tesla C1060の仕様

SM数 30 CUDA Core数 240(=8 Core/SM×30 SM) キャッシュを搭載せず

13

*CUDAのサポートから外れます

2016/01/13

SP SP

SP SP

SP SP

SP SP

SFU SFU16 KB

Shared Memory

Register File (16384×32‐bit)

Streaming Multiprocessor

SMSMSM

Page 14: GPGPU Seminar (GPGPU and CUDA Fortran)

Teslaアーキテクチャの構造

GPGPU講習会

Tesla C1060の仕様

CUDAコア数(単精度) 240 Cores

CUDAコアクロック周波数 1,296 MHz

単精度演算ピーク性能 622*1 (933*2) GFLOPS

倍精度演算ユニット数 30*3 Units

倍精度演算ピーク性能 78 GFLOPS

メモリクロック周波数 800 MHz

メモリバス幅 512 bit

大メモリバンド幅*4 102 GB/s

*1単精度演算ピーク性能 = コアクロック周波数×コア数×命令の同時発行数(2)*2CUDA CoreとSFUが同時に命令を発行できれば1296 MHz×240×3*3一つのSMに倍精度演算器が一つ搭載(と言われている)

*4 大メモリバンド幅=メモリクロック周波数×メモリバス幅/8×2(Double Data Rate)

14 2016/01/13

Page 15: GPGPU Seminar (GPGPU and CUDA Fortran)

Fermiアーキテクチャ

GPGPU講習会

Tesla M2050の仕様

SM数 14 CUDA Core数 448(=32 Core/SM×14 SM) L1/L2 キャッシュを搭載

ECC(誤り訂正機能)を搭載

15 2016/01/13

Register File (16384 × 32‐bit)

64 KB Shared Memory / L1 Cache

SM

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU×4

L2 Cache

GigaThread Engine

PCI Express 3.0 Host Interface

Memory Controller

GPCRaster Engine

GPCRaster Engine

SM

Raster EngineGPC

Raster EngineGPC

Memory ControllerMemory Controller

Memory ControllerMemory Controller

Memory Controller

詳細はhttp://www.nvidia.co.jp/docs/IO/81860/NVIDIA_Fermi_Architecture_Whitepaper_FINAL_J.pdfを参照のこと

Page 16: GPGPU Seminar (GPGPU and CUDA Fortran)

Fermiアーキテクチャの構造

GPGPU講習会

Tesla M2050の仕様

CUDAコア数(単精度) 448 Cores

CUDAコアクロック周波数 1,150 MHz

単精度演算ピーク性能 1.03 TFLOPS

倍精度演算ユニット数 0*1 Unit

倍精度演算ピーク性能 515 GFLOPS

メモリクロック周波数 1.55 GHz

メモリバス幅 384 bit

大メモリバンド幅 148 GB/s

*1単精度CUDA Coreを2基使って倍精度演算を実行

16 2016/01/13

Page 17: GPGPU Seminar (GPGPU and CUDA Fortran)

Keplerアーキテクチャの構造

GPGPU講習会

Tesla K20c/mの仕様

SMX数 13 Streaming Multiprocessor eXtreme (?)

CUDA Core数 2,496(=192 Core/SM×13 SMX)

17 2016/01/13

詳細はhttps://www.nvidia.co.jp/content/apac/pdf/tesla/nvidia‐kepler‐gk110‐architecture‐whitepaper‐jp.pdfを参照のこと

Register File (65536 × 32‐bit)

64 KB Shared Memory / L1 Cache

48 KB Read‐Only Data Cache

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

DP Unit

SMX

SMX

L2 Cache

GigaThread Engine

PCI Express 3.0 Host Interface

Memory ControllerMemory Controller

Memory Controller

Memory ControllerMemory Controller

Memory Controller

Page 18: GPGPU Seminar (GPGPU and CUDA Fortran)

Keplerアーキテクチャの構造

GPGPU講習会

Tesla K20c/mの仕様

CUDAコア数(単精度) 2,496 Cores

CUDAコアクロック周波数 706 MHz

単精度演算ピーク性能 3.52 TFLOPS

倍精度演算ユニット数 832*1 Units

倍精度演算ピーク性能 1.17 TFLOPS

メモリクロック周波数 2.6 GHz

メモリバス幅 320 bit

大メモリバンド幅 208 GB/s

*164基/SMX×13基

18 2016/01/13

Page 19: GPGPU Seminar (GPGPU and CUDA Fortran)

Maxwellアーキテクチャ

GeForce GTX TITAN Xの仕様

SM数 24 CUDA Core数 3,072(=128 Core/SM×24 SM)

GPGPU講習会19 2016/01/13

第1世代の詳細はhttps://www.nvidia.co.jp/content/product‐detail‐pages/geforce‐gtx‐750‐ti/geforce‐gtx‐750ti‐whitepaper.pdfを参照のこと

64 KB Shared Memory 

L1 Cache

SMM

Register File (16,384 × 32‐

bit)Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

L1 Cache

Register File (16,384 × 32‐

bit)Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

Register File (16,384 × 32‐

bit)Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

Register File (16,384 × 32‐

bit)Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU

SFU

SFU

SFU

SFU

SFU

SFU

SFU

PolyMorph Engine 3.0

SMM

Raster EngineGPC

Raster EngineGPC

L2 Cache

GigaThread EnginePCI Express 3.0 Host Interface

Memory Controller

Raster EngineGPC

Raster EngineGPC

Memory Controller

Memory ControllerMemory Controller

Page 20: GPGPU Seminar (GPGPU and CUDA Fortran)

Maxwellアーキテクチャ

GeForce GTX TITAN Xの仕様*

CUDAコア数(単精度) 3,072 Cores

CUDAコアクロック周波数 1,002 MHz

単精度演算ピーク性能 6.14 TFLOPS

倍精度演算ユニット数 0*1 Units

倍精度演算ピーク性能 192 GFLOPS*2

メモリクロック周波数 3.5 GHz*3

メモリバス幅 384 bit

大メモリバンド幅 336.5 GB/s

*1http://www.4gamer.net/games/121/G012181/20141225075/*2倍精度演算は単精度演算の性能の1/32 (1/16 Flop/Core/clock)*3DDR(Double Data Rate) 7GHz相当と書かれている場合もある

GPGPU講習会

http://http://www.geforce.com/hardware/desktop‐gpus/geforce‐gtx‐titan‐x/specifications

*http://ja.wikipedia.org/wiki/FLOPS

20 2016/01/13

Page 21: GPGPU Seminar (GPGPU and CUDA Fortran)

Pascalアーキテクチャ

2016年にリリース予定

倍精度演算器を搭載予定

NVLink GPU同士やGPUとCPUを接続する独自の方式

通信(CPU ↔ メモリ ↔ PCI Express ↔ メモリ ↔ GPU)のボトルネックを解消(PCI Express3.0の5~12倍)

複数のGPUを使って大規模な計算が可能

3Dメモリ(High Bandwidth Memory, HBM)* 3次元積層技術を利用し,メモリの容量と帯域を大幅に増加

大32GB,メモリ帯域1TB/s

GPGPU講習会

*http://pc.watch.impress.co.jp/docs/column/kaigai/20150421_698806.html

21 2016/01/13

Page 22: GPGPU Seminar (GPGPU and CUDA Fortran)

Voltaアーキテクチャ

Pascalの後継

詳しい情報は不明

アメリカの次世代スーパーコンピュータへ採用予定

オークリッジ国立研究所 SUMMIT 150~300PFLOPS ローレンス・リバモア研究所 SIERRA 100PFLOPS以上

地球シミュレータと同等の演算性能を1ノードで実現

現在Top500 2位のスーパーコンピュータと同じ電力で5~10倍高速,サイズは1/5

GPGPU講習会

*http://www.4gamer.net/games/121/G012181/20141225075/

22 2016/01/13

Page 23: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの普及の要因

GPGPU講習会23

GPUの進展は15年程

普及の速度は驚異的

CPUは数十年かけて進展

CPUも驚異的な速度で進展

様々な高速化技術を導入

GPUが普及している要因は何か?

2016/01/13

Page 24: GPGPU Seminar (GPGPU and CUDA Fortran)

TOP500 List(2015, Nov.) スーパーコンピュータの性能の世界ランキング

GPUを搭載したコンピュータは2基だけ

GPGPU講習会24

http://www.top500.org/より引用

2016/01/13

計算機名称(設置国) アクセラレータ実効性能[PFlop/s]

/ピーク性能[PFlop/s] 消費電力[MW]

1 Tianhe‐2 (China) Intel Xeon Phi 33.9/54.9 17.82 Titan (U.S.A.) NVIDIA K20x 17.6/27.1 8.203 Sequoia (U.S.A.) − 17.2/20.1 7.904 K computer (Japan) − 10.5/11.3 12.75 Mira (U.S.A.) − 8.59/10.1 3.956 Trinity (U.S.A.) − 8.10/11.17 Piz Daint (Switzerland) NVIDIA K20x 6.27/7.79 2.338 Hazel Hen (Germany) ‐ 5.64/7.409 Shaheen II(Saudi Arabia) ‐ 5.54/7.24 2.8310 Stampede (U.S.A.) Intel Xeon Phi 5.17/8.52 4.51

Page 25: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUの性能向上サイクル

GPGPU講習会25

半導体回路の細線化

消費電力が低下

低下分の電力をトランジスタのスイッチングに利用

動作周波数向上

性能向上

2016/01/13

ムーアの法則

Page 26: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUの性能向上サイクル

GPGPU講習会26

半導体回路の細線化

消費電力が低下

低下分の電力をトランジスタのスイッチングに利用

動作周波数向上

性能向上

絶縁部が狭くなり漏れ電流が発生,電力が低下しない

消費電力の増加によって発熱量が増加,空冷の限界

2倍のトランジスタ

を使っても性能は1.4倍程度にしか伸びない

2016/01/13

ムーアの法則

ポラックの法則

Page 27: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUの性能向上サイクル

GPGPU講習会27

半導体回路の細線化

消費電力が低下

低下分の電力をトランジスタのスイッチングに利用

動作周波数向上

性能向上

絶縁部が狭くなり漏れ電流が発生,電力が低下しない

消費電力の増加によって発熱量が増加,空冷の限界

2倍のトランジスタ

を使っても性能は1.4倍程度にしか伸びない

コア数の増加

2016/01/13

ムーアの法則

ポラックの法則

Page 28: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUの性能向上

FLOPS =  1コアの演算性能× コア数× CPUの動作周波数

1コアの演算性能の向上

演算器(トランジスタ)の増加

コア数の増加

トランジスタ数の増加

CPUの動作周波数

回路の効率化や印可電圧の向上劇的な性能向上は期待できない

コンパイラの 適化を利用

複数のコアを使うようにプログラムを書かないと速くならない

GPGPU講習会28 2016/01/13

Page 29: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUを使うという選択

GPU普及の要因の一つはCPUクロックの頭打ち

クロックを下げてマルチコア化したCPUへの対応が必要

なぜGPUという選択か?

CPU用プログラムの並列化でもいいのでは?

消費電力の低減

数値計算や高性能計算(HPC)の業界がGPUに注目

スーパーコンピュータの性能向上

高機能なCPUを大量に使うと消費電力が問題に

高機能な制御用プロセッサと,計算を実行する低性能なアクセラレータの組み合わせ

GPGPU講習会29 2016/01/13

Page 30: GPGPU Seminar (GPGPU and CUDA Fortran)

Green500(2015, Nov.) TOP3の計算機がそれぞれ異なるアクセラレータを搭載

インターネットのサービス提供に利用されている(と思われる)計算機が大量にランクイン

GPGPU講習会30http://www.green500.org/より引用

2016/01/13

計算機名称 アクセラレータ GFLOPS/W 消費電力[kW]

1 Shoubu PEZY‐SC 7.03 50.322 TSUBAME‐KFC NVIDIA Tesla K80 5.33 51.133 ASUS ESC4000  AMD FirePro S9150 5.27 57.154 Sugon Cluster  NVIDIA Tesla K80 4.78 65.005 Xstream NVIDIA Tesla K80 4.11 190.06 Inspur TS10000 NVIDIA Tesla K40 3.86 58.007 Inspur TS10000 NVIDIA Tesla K40 3.78 110.08 Inspur TS10000 NVIDIA Tesla K40 3.78 110.09 Inspur TS10000 NVIDIA Tesla K40 3.78 110.010 Inspur TS10000 NVIDIA Tesla K40 3.78 110.0

Page 31: GPGPU Seminar (GPGPU and CUDA Fortran)

まとめ

GPGPU講習会31

GPUの特徴

低性能の演算器を大量に搭載(~3000コア)

GPUが使われる理由

理論演算性能が高い

メモリとチップ間の帯域も広い

省電力と高性能を両立

今後の計算機の主流になると考えられる

将来に対する投資

GPUだけでなく,制御用CPU+計算用アクセラレータという思想は今後しばらく主流であり続ける

2016/01/13

Page 32: GPGPU Seminar (GPGPU and CUDA Fortran)

GROUSEの利用方法

Page 33: GPGPU Seminar (GPGPU and CUDA Fortran)

情報処理センターGPGPUシステム

16台の計算サーバで構成

NVIDIA Tesla M2050を搭載

各サーバに4機ずつ計64機 grouse

tesla01 ‐ tesla04

M2050×16

tesla05 ‐ tesla08

M2050×16

tesla09 – tesla12

M2050×16

tesla13 – tesla16

M2050×16

外部ネットワーク

GPGPU講習会33 2016/01/13

Page 34: GPGPU Seminar (GPGPU and CUDA Fortran)

grouseへのログイン

2016/01/13GPGPU講習会34

2013年度GPGPU講習会資料より引用

Page 35: GPGPU Seminar (GPGPU and CUDA Fortran)

grouseへのログイン

2016/01/13GPGPU講習会35

2013年度GPGPU講習会資料より引用

統合アカウントとパスワード入力

Page 36: GPGPU Seminar (GPGPU and CUDA Fortran)

ターミナルの起動

2016/01/13GPGPU講習会36

2013年度GPGPU講習会資料より引用

System→Terminal

Page 37: GPGPU Seminar (GPGPU and CUDA Fortran)

ターミナルの起動

2016/01/13GPGPU講習会37

2013年度GPGPU講習会資料より引用

Page 38: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse

処理

処理結果出力

キー入力,マウス入力

2016/01/13GPGPU講習会38

Page 39: GPGPU Seminar (GPGPU and CUDA Fortran)

プログラムの作成と保存

2016/01/13GPGPU講習会39

2013年度GPGPU講習会資料より引用

Page 40: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse

ファイル保存

ハードディスク

共有

共有

2016/01/13GPGPU講習会40

tesla01 ‐ tesla04 tesla05 ‐ tesla08 tesla09 – tesla12 tesla13 – tesla16

共有

Page 41: GPGPU Seminar (GPGPU and CUDA Fortran)

tesla??へのログイン

もう一つターミナルを起動し,下のターミナルでログイン$ ssh␣–l␣ユーザID␣tesla??(??には01~16の番号を入力)

2016/01/13GPGPU講習会41

2013年度GPGPU講習会資料より引用

Page 42: GPGPU Seminar (GPGPU and CUDA Fortran)

tesla??でのコンパイルとプログラム実行

2016/01/13GPGPU講習会42

grouseでの作業用

tesla??でのコンパイルと実行用

2013年度GPGPU講習会資料より引用

Page 43: GPGPU Seminar (GPGPU and CUDA Fortran)

tesla??でコンパイルとプログラム実行

コンパイル

$ pgf90˽ソースファイル名

pgf90がコンパイル対象とするソースファイルの拡張子は.cuf エラーがなければa.outという実行ファイルが作成される

CUDAに関係するオプションは‐Mcuda=...で指定

実行

$ ./a.out

対応オプションの一覧は‐helpオプションで確認

$ pgf90˽‐help

2016/01/13GPGPU講習会43

Page 44: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse

処理

処理結果出力

キー入力,マウス入力

$ ssh␣tesla16

tesla16

キー入力

処理処理結果

2016/01/13GPGPU講習会44

Page 45: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse

キー入力,マウス入力

$ ssh␣tesla16$ pgf90 ??.cuf

キー入力

処理結果

ハードディスク

??.cuf

コンパイル

2016/01/13GPGPU講習会

処理結果出力

tesla1645

Page 46: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse処理結果出力

キー入力,マウス入力

$ ssh␣tesla16$ pgf90 ??.cuf$ ./a.out

キー入力

処理結果

ハードディスク

a.out

実行

2016/01/13GPGPU講習会

tesla1646

Page 47: GPGPU Seminar (GPGPU and CUDA Fortran)

実行イメージ

grouse

キー入力,マウス入力

$ ssh␣tesla16$ pgf90 ??.cuf$ ./a.out

キー入力

ハードディスク

a.out

実行

2016/01/13GPGPU講習会

tesla16GPU0GPU1GPU2GPU3

処理結果出力

処理結果

47

Page 48: GPGPU Seminar (GPGPU and CUDA Fortran)

情報処理センターでのCUDAの使い方

1. grouseやtesla??で開発する場合

grouseやtesla??にログイン

ソースファイルを作成し,tesla??上でコンパイル・実行

grouseはGPUを搭載していないため実行できない(コンパイルは可能)

2. 研究室のPC等,情報処理センター外で開発する場合

研究室のPCでソースファイルを作成

WinSCPなどでファイルをgrouseにアップロード

grouseを経由してtesla??にログインした後,コンパイル・実行

2016/01/13GPGPU講習会48

Page 49: GPGPU Seminar (GPGPU and CUDA Fortran)

ターミナルのみの利用(特に演習室外から利用する場合)

1. Tera Termでgrouseにログイン

2013年度GPGPU講習会資料より引用

2016/01/13GPGPU講習会49

Page 50: GPGPU Seminar (GPGPU and CUDA Fortran)

ターミナルのみの利用(特に演習室外から利用する場合)

2. sshでtesla??にログイン

$ ssh␣–l␣ユーザID␣tesla??(??には01~16の番号を入力)

2013年度GPGPU講習会資料より引用

2016/01/13GPGPU講習会50

Page 51: GPGPU Seminar (GPGPU and CUDA Fortran)

grouseへのファイル転送(WinSCP) Unixコマンドscp(secure copy)のWindows GUIクライアント

Secure Shell (ssh) に含まれるsshの機能を利用して安全性の高いファイル転送を行う

Host名 grouse

統合アカウントのユーザ名とパスワード

login2016/01/13GPGPU講習会51

Page 52: GPGPU Seminar (GPGPU and CUDA Fortran)

Windows上のソースファイルをコピー

grouseハードディスク

WinSCPでコピー

ハードディスク

共有

共有

2016/01/13GPGPU講習会

ソースファイル

52

tesla01 ‐ tesla04 tesla05 ‐ tesla08 tesla09 – tesla12 tesla13 – tesla16

共有

ソースファイル

Page 53: GPGPU Seminar (GPGPU and CUDA Fortran)

ログインしているサーバの確認

grouseにログインしているかtesla??にログインしているか分からなくなったら

$ hostname

2016/01/13GPGPU講習会53

Page 54: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの選択

計算機がGPUを複数搭載している場合

CUDAで利用するGPUを選択

CUDA APIを利用したGPUの選択

cudaSetDevice()命令

GPGPU講習会

program mainuse cudafor変数宣言 !ここでは標準でGPU0が使われるGPUやCPUを使った処理 !stat = cudaSetDevice(3) !ここからGPU3が使われる...  

end program main

54 2016/01/13

Page 55: GPGPU Seminar (GPGPU and CUDA Fortran)

長岡技術科学大学 電気電子情報工学専攻 出川智啓

GPUのプログラム構造

Page 56: GPGPU Seminar (GPGPU and CUDA Fortran)

今回の内容

GPGPU講習会

GPUプログラミング環境(CUDA)

GPUプログラムの実行の流れ

CUDAによるプログラムの記述

カーネル(GPUで処理する関数)の構造

記述方法とその理由

GPU固有のパラメータの確認

56 2016/01/13

Page 57: GPGPU Seminar (GPGPU and CUDA Fortran)

GPU(Graphics Processing Unit)とは

画像処理専用のハードウェア

具体的には画像処理用のチップ

チップ単体では販売されていない

PCI‐Exカードで販売

チップ単体と区別せずにGPUと呼ぶことも多い

ノートPCに搭載

PCI‐Exカードとして販売されるGPUには,ビデオメモリと呼ばれるDRAMが搭載

2016/01/13GPGPU講習会57

Page 58: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUのハードウェア構造

CUDA Core(旧Streaming Processor, SP)と呼ばれる演算器を多数搭載

Streaming Multiprocessor(SM, SMX)が複数のCUDA CoreとSFU,メモリをまとめて管理

SFU(Special Function Unit) 数学関数を計算するユニット

複数のSMが集まってGPUを構成

2016/01/13GPGPU講習会58

Page 59: GPGPU Seminar (GPGPU and CUDA Fortran)

Fermiアーキテクチャ

2016/01/13GPGPU講習会59

Tesla M2050の仕様

SM数 14 CUDA Core数 448(=32 Core/SM×14 SM)

動作周波数 1,150 MHz 単精度演算ピーク性能 1.03 TFLOPS

Register File (16384 × 32‐bit)

64 KB Shared Memory / L1 Cache

SM

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

SFU×4

L2 Cache

GigaThread Engine

PCI Express 3.0 Host Interface

Memory Controller

GPCRaster Engine

GPCRaster Engine

SM

Raster EngineGPC

Raster EngineGPC

Memory ControllerMemory Controller

Memory ControllerMemory Controller

Memory Controller

詳細はhttp://www.nvidia.co.jp/docs/IO/81860/NVIDIA_Fermi_Architecture_Whitepaper_FINAL_J.pdfを参照のこと

Page 60: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA CoreCUDA CoreCUDA CoreCUDA CoreCUDA CoreCUDA CoreCUDA CoreCUDA Core

Streaming Multiprocessor

GPUの模式図

2016/01/13GPGPU講習会60

GPU

Streaming Multiprocessor

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPUChip

グローバルメモリ

SM SM SM SM・・・

・・・

SM SM SM SM・・・ レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

StreamingMultiprocessor

SM SM SM SM・・・

ローカルメモリ

ローカルメモリ ・・・

ローカルメモリ

ローカルメモリ ・・・

Page 61: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA Compute Unified Device Architecture NVIDIA社製GPU向け開発環境(Windows,Linux,Mac OS X) 2007年頃発表

C/C++言語+独自のGPU向け拡張

専用コンパイラ(nvcc)とランタイムライブラリ

いくつかの数値計算ライブラリ(線形代数計算,FFTなど)

CUDA登場以前

グラフィクスプログラミングを利用

足し算を行うために,色を混ぜる処理を実行

汎用計算のためには多大な労力が必要

GPGPU講習会61 2016/01/13

Page 62: GPGPU Seminar (GPGPU and CUDA Fortran)

プログラマブルシェーダを用いた汎用計算

グラフィックスAPI(DirectX, OpenGL)による描画処理+シェーダ言語(HLSL, GLSL)による演算

void gpumain(){vec4 ColorA = vec4(0.0, 0.0, 0.0, 0.0); vec4 ColorB = vec4(0.0, 0.0, 0.0, 0.0);vec2 TexA = vec2(0.0, 0.0); vec2 TexB = vec2(0.0, 0.0);TexA.x = gl_FragCoord.x; TexA.y = gl_FragCoord.y;TexB.x = gl_FragCoord.x; TexB.y = gl_FragCoord.y;ColorA = texRECT( texUnit0, TexA );ColorB = texRECT( texUnit1, TexB );gl_FragColor = F_ALPHA*ColorA + F_BETA*ColorB;

}

void main(){glutInit( &argc, argv );glutInitWindowSize(64,64);glutCreateWindow("GpgpuHelloWorld");glGenFramebuffersEXT(1, &g_fb);glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_fb);glGenTextures(4, g_nTexID); // create (reference to) a new textureglBindTexture(opt1, texid);glTexParameteri(opt1, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri(......);glTexImage2D(opt1, 0, opt2, width, height, 0, GL_RGBA, GL_FLOAT, 0);……(以下省略)

GPUの処理(GLSL)各ピクセルに対して実行

CPUの処理(OpenGL)

シェーダ言語を用いた配列加算(c=*a + *b)の例

GPGPU講習会62 2016/01/13

Page 63: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA Fortran

2016/01/13GPGPU講習会63

FortranのNVIDIA GPU向け拡張

PGI社の販売するFortranコンパイラで利用可能

10.0以降で利用可能

2016年1月7日現在の 新版は15.10

CUDA Cを利用するが,新機能はFortranコンパイラが対応しないと利用できない

CUDA Cのバージョンが4.0でも,PGIコンパイラのバージョンが古いとバージョン3.2までしか利用できない

Page 64: GPGPU Seminar (GPGPU and CUDA Fortran)

FortranによるGPGPU

2016/01/13GPGPU講習会64

GPGPUの普及と裾野の広がり

FORTRANからC言語を経由してGPUへ移植

資産を多く持つFortranユーザからの要求の高まり

新しい概念(GPUのプログラミングモデル)と開発言語の習得は高負荷

CUDA Fortranの登場

かけた労力と得られる利得(性能向上)のバランスがよい

並列計算の知識だけである程度の性能が得られる

Page 65: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA Fortran

2016/01/13GPGPU講習会65

CUDA Cと比較してコーディングが簡単

CPUでプログラムを組む様な感覚

GPUの制御を隠して数値計算に集中

ライブラリよりも手間はかかるがチューニングが可能

労力(チューニング)と利得(高速化)の比較

CUDA Cよりも労力を少なく

Page 66: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAによるプログラミング

CPUをホスト(Host),GPUをデバイス(Device)と表現

ホスト(CPU) 処理の流れやGPUを利用するための手続きを記述

プログラムの書き方は従来のFortranと同じ

利用するGPUの決定,GPUへのデータ転送,GPUで実行する関数の呼び出し等

GPGPU講習会66 2016/01/13

Page 67: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAによるプログラミング

CPUをホスト(Host),GPUをデバイス(Device)と表現

デバイス(GPU) 処理する内容を関数として記述

引数は利用可能,返値は利用不可(subroutineを使用)

関数はkernelと呼ばれる

関数呼び出しはlaunch, invokeなどと呼ばれる

GPGPU講習会67 2016/01/13

Page 68: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello World 何を確認するか

小構成のプログラムの作り方

ファイル命名規則(拡張子は.f90) コンパイルの方法(gfortran, pgf90等を使用)

program mainimplicit none

print *,"hello world"

end program main

GPGPU講習会68 2016/01/13

helloworld.f90

Page 69: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA FortranでHello World 何を確認するか

小構成のプログラムの作り方

ファイル命名規則(拡張子は.cuf) コンパイルの方法(pgf90を使用)

program mainimplicit none

print *,"hello world"

end program main

GPGPU講習会69

program mainimplicit none

print *,"hello world"

end program main違いは拡張子だけ?

2016/01/13

helloworld.cuf helloworld.f90

Page 70: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA Fortranプログラムのコンパイル

ソースファイルの拡張子は.cuf

pgf90を用いてコンパイル* プリプロセッサがソースをホストコード(CPUが処理する内容)とデバイスコード(GPUが処理する内容)に分離

ホストコードはPGIコンパイラがコンパイル

デバイスコードはCUDA Cのコードへと変換

helloworld.cufにはCPUで処理する箇所しかない

GPGPU講習会70 2016/01/13

*伊藤智義 編,GPUプログラミング入門,講談社,2013,p.142.

Page 71: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDA FortranでHello World CUDA Fortran専用の処理を追加

2016/01/13GPGPU講習会71

module cuf_kernelimplicit nonecontains

attributes(global) subroutine kernel()end subroutine kernelend module cuf_kernel

program mainuse cudaforuse cuf_kernelimplicit none

call kernel<<<1,1>>>()print *,"hello world"

end program main

GPUで実行されるサブルーチン

(カーネル)attributes(global)が追加

・・・

通常のサブルーチン呼出とは異なり,<<<>>>が追加

・・・

helloworld_kernel.cuf

Page 72: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAプログラムの実行

実行時の流れ(CPU視点)

利用するGPUの初期化やデータの転送などを実行

GPUで実行する関数を呼び出し

GPUから結果を取得

初期化の指示初期化

カーネルの実行指示カーネルを実行

結果の取得実行結果をコピー

time

CPUとGPUは非同期CPUは別の処理を実行可能

GPGPU講習会72

必要なデータのコピーメモリに書込

2016/01/13

Page 73: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Thread(Fermi世代以降)

print文をGPUから呼び出し,並列に実行

2016/01/13GPGPU講習会73

module kernelimplicit nonecontains

subroutine hello()print *,"Hello Thread"

end subroutine helloend module kernel

program main

use kernelimplicit none

call hello()

end program main

画面表示・・・

サブルーチン実行・・・

hellothread.f90

Page 74: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Thread(Fermi世代以降)

GPUの各スレッドが画面表示

2016/01/13GPGPU講習会74

module cuf_kernelimplicit nonecontains

attributes(global) subroutine hello()print *,"Hello Thread"

end subroutine helloend module cuf_kernel

program mainuse cudaforuse cuf_kernelimplicit noneinteger :: statcall hello<<<1,1>>>()stat = cudaThreadSynchronize()

end program main

画面表示(Fermi世代以降で可能)コンパイル時にオプションが必要‐Mcuda=cc20以降

・・・

カーネル実行・・・

ホストとデバイスの同期をとるCPUとGPUは原則同期しないので, 同期しないとカーネルを実行した直後にプログラムが終了

・・・

hellothread.cuf

Page 75: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAでカーネルを作成するときの制限

2016/01/13GPGPU講習会75

print文による画面出力

Fermi世代以降のGPUで,コンパイルオプションを付与

‐Mcuda=cc{20|2x|2+|30|3x|35|50}

エミュレーションモード

GPUの動作(並列実行)をCPUで模擬

CUDA4.0以降では消滅

オプション付きのコンパイル

pgf90 ‐Mcuda=cc20 hellothread.cuf カーネル内でprintを使うコードは,GROUSEではコンパイルが通りません

Page 76: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUプログラムへの変更

2016/01/13GPGPU講習会76

変更点

サブルーチンの前にattributes(global)をつけた

Page 77: GPGPU Seminar (GPGPU and CUDA Fortran)

変更の理由

2016/01/13GPGPU講習会77

変更点

サブルーチンの前にattributes(global)をつけた

変更によって実現されること

GPUで実行する関数という目印になる

変更が必要な理由

ホスト(CPU)からGPUで実行する関数(カーネル)を呼び出し

CPUが処理する箇所とGPUが処理する箇所は別のコンパイラがコンパイル

コンパイルの時点でどれがカーネルかを明記

Page 78: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUプログラムへの変更

2016/01/13GPGPU講習会78

変更点(ではないが・・・)

helloをfunctionではなくsubroutineとした

Page 79: GPGPU Seminar (GPGPU and CUDA Fortran)

変更の理由

2016/01/13GPGPU講習会79

変更点(ではないが・・・)

helloをfunctionではなくsubroutineとした

変更によって実現されること

GPUのハードウェア構造に適したプログラムを作成できる

変更が必要な理由

GPUはホストと別に独立したメモリを持つ

GPUは描画情報を受け取り,画面に出力

GPU→CPUの頻繁なデータ転送は苦手

プログラマがメモリ管理を行い,無駄なデータ転送による実行速度低下を回避

描画情報画面出力

Page 80: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUプログラムへの変更

2016/01/13GPGPU講習会80

変更点

サブルーチン呼出の際にサブルーチン名と引数の間に<<<1,1>>>を付けた

Page 81: GPGPU Seminar (GPGPU and CUDA Fortran)

変更の理由

2016/01/13GPGPU講習会81

変更点

サブルーチン呼出の際にサブルーチン名と引数の間に<<<1,1>>>を付けた

変更によって実現されること

GPUのハードウェア構造に適したプログラムを作成できる

変更が必要な理由

GPUには数百から数千のCUDAコアが搭載されており,それらが協調して並列処理を実行

1スレッドが実行する処理を書くことでカーネルの作成を簡略化

並列処理の度合いはカーネル呼出の際に指定

Page 82: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUプログラムへの変更

2016/01/13GPGPU講習会82

変更点

カーネルを呼び出した後に同期を取る関数を呼んだ

Page 83: GPGPU Seminar (GPGPU and CUDA Fortran)

変更の理由

2016/01/13GPGPU講習会83

変更点

カーネルを呼び出した後に同期を取る関数を呼んだ

変更によって実現されること

GPUで実行した結果が正しく得られる

変更が必要な理由

CPUとGPUは非同期に処理を実行

関数を呼んでCPU側に制御が戻った直後にプログラムが終了(画面表示が行われない)

正しい結果を得るためにカーネルの終了を待つ

Page 84: GPGPU Seminar (GPGPU and CUDA Fortran)

<<< , >>>内の数字で並列度が変わることの確認module cuf_kernel

implicit nonecontains

attributes(global) subroutine hello()print *,"Hello Thread"

end subroutine helloend module cuf_kernel

program mainuse cudaforuse cuf_kernelimplicit noneinteger :: statcall hello<<<?,?>>>()stat = cudaThreadSynchronize()

end program main

Hello Thread(Fermi世代以降)

2016/01/13GPGPU講習会84

<<<>>>内の数字を変えると画面表示される行数が変わる<<<1,8>>>, <<<8,1>>>, <<<4,2>>>

・・・

hellothread.cuf

Page 85: GPGPU Seminar (GPGPU and CUDA Fortran)

<<<,>>>内の2個の数字の意味は?

GPUのハードウェアの構成に対応させて並列性を管理

各階層における並列実行の度合を指定

<<<,>>>内に2個の数字を記述して,各階層の並列度を指定

GPU

Streaming Multiprocessor

CUDA Core

ハードウェア構成

並列に実行する処理

スレッドの集まり

スレッド

並列化の階層

Grid

Thread Block

Thread

CUDA

2016/01/13GPGPU講習会85

Page 86: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの並列化の階層

グリッド-ブロック-スレッドの3階層

グリッド(Grid) 並列に実行する処理

GPUが処理を担当する領域全体

スレッド(Thread) GPUの処理の基本単位

CPUのスレッドと同じ

ブロック(Block)もしくはスレッドブロック(Thread Block)* スレッドの集まり

GPGPU講習会86 2016/01/13

*スレッドブロックだと長い上にスレッドや変数名との兼ね合いで混乱を招くのでブロックで統一

Page 87: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの並列化の階層

各階層の情報を参照できる変数 x,y,zを成分(component)にもつdim3派生型

グリッド(Grid) gridDim グリッド内にあるブロックの数

ブロック(Block) blockIdx ブロックに割り当てられた番号

blockDim ブロック内にあるスレッドの数

スレッド(Thread) threadIdx スレッドに割り当てられた番号

GPGPU講習会87 2016/01/13

Page 88: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads(Fermi世代以降)

<<< >>>内の数字で表示される内容が変化

2016/01/13GPGPU講習会88

module cuf_kernelimplicit nonecontains

attributes(global) subroutine hello()print *,"gridDim'%'x",gridDim%x,"blockIdx'%'x",blockIdx%x,&

"blockDim'%'x",blockDim%x,"threadIdx'%'x",threadIdx%xend subroutine helloend module cuf_kernelprogram main

use cudaforuse cuf_kernelimplicit noneinteger :: statcall hello<<<2,4>>>()stat = cudaThreadSynchronize()

end program main

<<<>>>内の数字を変えると画面表示される内容が変わる<<<>>>内の数字とどのパラメータが対応しているかを確認

・・・

hellothreads.cuf

Page 89: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの構造とカーネルの書き方

GPUはマルチスレッド(メニースレッド)で並列処理

数百から数千のCUDAコアが搭載されており,それらが協調して並列処理を実行

カーネルには1スレッドが実行する処理を書く

カーネルの作成を簡略化

カーネルを呼び出す際に並列処理の度合いを指定

カーネルと引数の間に追加した<<<,>>>で並列処理の度合を指定

GPGPU講習会89 2016/01/13

Page 90: GPGPU Seminar (GPGPU and CUDA Fortran)

各階層の値の設定

設定の条件

GPUの世代によって設定できる上限値が変化

確認の方法

pgaccelinfo deviceQuery

GPU Computing SDKに含まれているサンプル

CUDA Programming Guide https://docs.nvidia.com/cuda/cuda‐c‐programming‐guide/#compute‐capabilities

2016/01/13GPGPU講習会90

Page 91: GPGPU Seminar (GPGPU and CUDA Fortran)

pgaccelinfoの実行結果Device Number:                 0Device Name:                   Tesla M2050Device Revision Number:        2.0Global Memory Size:            2817982464Number of Multiprocessors:     14Number of Cores:               448Concurrent Copy and Execution: YesTotal Constant Memory:         65536Total Shared Memory per Block: 49152Registers per Block:           32768Warp Size:                     32Maximum Threads per Block:     1024Maximum Block Dimensions:      1024, 1024, 64Maximum Grid Dimensions:       65535 x 65535 x 65535Maximum Memory Pitch:          2147483647BTexture Alignment:             512BClock Rate:                    1147 MHzInitialization time:           4222411 microsecondsCurrent free memory:           2746736640Upload time (4MB):             2175 microseconds ( 829 ms pinned)Download time:                 2062 microseconds ( 774 ms pinned)Upload bandwidth:              1928 MB/sec (5059 MB/sec pinned)Download bandwidth:            2034 MB/sec (5418 MB/sec pinned)

2016/01/13GPGPU講習会91

Page 92: GPGPU Seminar (GPGPU and CUDA Fortran)

pgaccelinfo実行結果 Revision Number:       2.0

Global Memory Size:            2817982464

Warp Size:                     32

Maximum Threads per Block:     1024

Maximum Block Dimensions:      1024, 1024, 64

Maximum Grid Dimensions:       65535 x 65535 x 65535

GPUの世代(どのような機能を有しているか)

実行時のパラメータ

選択の際に重要

各方向の 大値

1ブロックあたりのスレッド数は 大1024 (1024, 1, 1), (1, 1024, 1)

(32, 32, 1), (4, 4, 64)など

2016/01/13GPGPU講習会92

Page 93: GPGPU Seminar (GPGPU and CUDA Fortran)

長岡技術科学大学 電気電子情報工学専攻 出川智啓

GPUでの並列プログラミング(ベクトル和)

Page 94: GPGPU Seminar (GPGPU and CUDA Fortran)

ベクトル和C=A+Bの計算

配列要素に対して計算順序の依存性がなく, も単純に並列化可能

配列a, b, cの配列要素番号iが同じ

・・・

・・・

・・・c(i)

a(i)

b(i)

+ + + + + +

GPGPU講習会94 2016/01/13

Page 95: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

subroutine add(a, b, c)implicit none

real :: a(N)real :: b(N)real :: c(N)

integer :: i

do i=1,Nc(i) = a(i) + b(i)

end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kernelimplicit none

real :: a(N)real :: b(N)real :: c(N)

a=1.0b=2.0c=0.0

call add(a,b,c)

end program main

CPUプログラム(メモリの静的確保)

GPGPU講習会95 2016/01/13

vectoradd.f90

Page 96: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

subroutine add(a, b, c)implicit none

real :: a(N)real :: b(N)real :: c(N)

integer :: i

do i=1,Nc(i) = a(i) + b(i)

end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kernelimplicit none

real,allocatable :: a(:)real,allocatable :: b(:)real,allocatable :: c(:)

allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

CPUプログラム(メモリの動的確保)

GPGPU講習会96 2016/01/13

vectoradd_allocate.f90

Page 97: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUプログラム(メモリの動的確保)

GPGPU講習会

allocate/deallocate 指定した要素数分のメモリを確保

allocateする変数はallocatable属性を付けて宣言

配列の次元は:で明記

多次元配列も確保可能

97

integer,allocatable :: a(:) !1次元配列allocate(a(100))deallocate(a)

2016/01/13

integer,allocatable :: a(:,:) !2次元配列allocate(a(100,200))deallocate(a)

Page 98: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUへの移植

GPGPU講習会98

ソースファイルの拡張子を.f90から.cufに変更

GPUの都合を反映(前回の講義資料参照)

サブルーチンの前にattributes(global)をつけた

GPUで実行する関数という目印にするため

functionではなくsubroutineを利用

GPUのハードウェア構造に適したプログラムを作るため

サブルーチン呼出の際にサブルーチン名と引数の間に<<<1,1>>>を付けた

GPUのハードウェア構造に適したプログラムを作るため

関数には1スレッドが処理する内容を書き,実行時の並列度を指定

カーネルを呼び出した後に同期を取る関数を呼んだ

GPUで実行した結果を正しく得るため

2016/01/13

Page 99: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUへの移植(メモリの取り扱い)

GPGPU講習会99

ベクトルa,b,cを確保し,a,bの値を読んでcに書き込む

ホスト(CPU)にはプロセッサとメモリが存在

デバイス(GPU)にもプロセッサとメモリが存在

デバイスからホストのメモリは(原則)直接読み書きできない

real :: a(N),b(N),c(N)

do i=1,Nprint *,a(i),b(i),c(i);

end do

abc

attributes(global) subroutine add()do i=1,Nc(i) = a(i)+b(i)

end doend subroutine add

2016/01/13

Page 100: GPGPU Seminar (GPGPU and CUDA Fortran)

attributes(global) subroutine add()do i=1,Nc(i) = a(i)+b(i)

end doend subroutine add

GPUへの移植(メモリの取り扱い)

GPGPU講習会100

ベクトルa,b,cを確保し,a,bの値を読んでcに書き込む

ホスト(CPU)にはプロセッサとメモリが存在

デバイス(GPU)にもプロセッサとメモリが存在

デバイスからホストのメモリは(原則)直接読み書きできない

GPUのメモリを確保し,カーネルから利用

do i=1,Nprint *,a(i),b(i),c(i);

end do

abc

2016/01/13

Page 101: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUのメモリの動的確保

2016/01/13GPGPU講習会101

GPU側のメモリに確保する変数にはdevice属性を付与

メモリの確保/解放はallocate/deallocateを利用

メモリの属性から判断して適切に処理

CUDA Cはメモリ確保/解放の関数をCUDA用に置き換え

integer,allocatable :: a(:,:)integer,allocatable,device :: b(:,;) !device属性を付与

allocate(a(N,N)) !CPU側のメモリに確保allocate(b(N,N)) !GPU側のメモリに確保,多次元配列も可能

deallocate(a) !メモリを解放deallocate(b) !

CUDA Fortranの利点の一つ

Page 102: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

subroutine add(a, b, c)implicit none

real :: a(N)real :: b(N)real :: c(N)

integer :: i

do i=1,Nc(i) = a(i) + b(i)

end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kernel

implicit none

real,allocatable :: a(:)real,allocatable :: b(:)real,allocatable :: c(:)

allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

CPUプログラム(メモリの動的確保)

GPGPU講習会102 2016/01/13

vectoradd_allocate.f90

Page 103: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

attributes(global)&subroutine add(a, b, c)

implicit none

real :: a(N)real :: b(N)real :: c(N)

integer :: i

do i=1,Nc(i) = a(i) + b(i)

end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kerneluse cudaforimplicit none

real,allocatable,device :: a(:)real,allocatable,device :: b(:)real,allocatable,device :: c(:)

allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add<<<1,1>>>(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

GPUプログラム(1スレッド実行版)

GPGPU講習会103 2016/01/13

vectoradd_1thread.cuf

Page 104: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

attributes(global)&subroutine add(a, b, c)

implicit none

real :: a(N)real :: b(N)real :: c(N)

integer :: i

do i=1,Nc(i) = a(i) + b(i)

end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kerneluse cudaforimplicit none

real,allocatable,device :: a(:)real,allocatable,device :: b(:)real,allocatable,device :: c(:)

allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add<<<1,1>>>(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

GPUプログラム(1スレッド実行版)

GPGPU講習会104

GPUカーネルの目印

並列実行の度合を指定

2016/01/13

vectoradd_1thread.cuf

device属性を付与

Page 105: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUプログラムへの変更

2016/01/13GPGPU講習会105

変更点

変数にdevice属性を付与

Page 106: GPGPU Seminar (GPGPU and CUDA Fortran)

変更の理由

2016/01/13GPGPU講習会106

変更点

変数にdevice属性を付与

変更によって実現されること

CPUとGPUのハードウェア制約を回避できる

変更が必要な理由

GPUはPCI‐Exバスを経由してホストと接続されている

GPUはホストと別に独立したメモリを持っている

GPUはホストメモリ(CPU側のメモリ)に直接アクセスできないため,GPUが持っているメモリを利用

Page 107: GPGPU Seminar (GPGPU and CUDA Fortran)

実行結果

GPGPU講習会107

実行時間の確認

CUDAの実行環境に組み込まれたプロファイラを利用

環境変数CUDA_PROFILEを1に設定する事で実行時間を測定

使い方

1. 環境変数の設定 $ export CUDA_PROFILE=1

2. プログラムの実行 $ ./a.out

3. プロファイル結果(標準はcuda_profile_0.log)の確認

2016/01/13

Page 108: GPGPU Seminar (GPGPU and CUDA Fortran)

実行結果

GPGPU講習会108

プロファイルの一連の流れ

method カーネルや関数(API)の名称

gputime GPU上で処理に要した時間(s単位)

cputime CPUで処理(=カーネル起動)に要した時間

実際の実行時間=cputime+gputime occupancy GPUがどれだけ効率よく利用されているか

‐bash‐3.2$ pgf90 vectoradd_1thread.cuf プログラムをコンパイル‐bash‐3.2$ export CUDA_PROFILE=1 環境変数CUDA_PROFILEを1にしてプロファイラを有効化‐bash‐3.2$ ./a.out プログラムを実行(cuda_profile_0.logというファイルが作られる)‐bash‐3.2$ cat cuda_profile_0.log cuda_profile_0.logの内容を画面に表示# CUDA_PROFILE_LOG_VERSION 2.0# CUDA_DEVICE 0 Tesla M2050# TIMESTAMPFACTOR fffff5f0d8759ef8method,gputime,cputime,occupancymethod=[ __pgi_dev_cumemset_4f ] gputime=[ 36.320 ] cputime=[ 16.000 ] occupancy=[ 1.000 ]method=[ __pgi_dev_cumemset_4f ] gputime=[ 35.200 ] cputime=[ 8.000 ] occupancy=[ 1.000 ]method=[ __pgi_dev_cumemset_4f ] gputime=[ 35.104 ] cputime=[ 7.000 ] occupancy=[ 1.000 ]method=[ add ] gputime=[ 206041.375 ] cputime=[ 6.000 ] occupancy=[ 0.021 ]

2016/01/13

Page 109: GPGPU Seminar (GPGPU and CUDA Fortran)

実行結果

GPGPU講習会109

計算が正しく行われているかの確認

配列c(:)の値が全て3.0になっていれば正しい

print文を使って表示

大量に画面表示されて煩わしい

GPUのカーネルには実行時間の制限がある

配列c(:)の値の平均を計算

平均が3.0になっていれば正しく実行できているだろうと推察

配列c(:)の平均をGPUで計算するのは難しい

CPUで配列c(:)の平均を計算

CPUはGPUのメモリを直接読み書きできない

CUDA Cでは専用の命令を使ってGPUからCPUへコピー

2016/01/13

Page 110: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUとGPUのやりとり

GPGPU講習会

単純なコピーであれば代入演算子(=)が利用可能

配列の全要素に同じ値を代入

配列要素数が同じ配列同士のコピー

コピー可能な方向

CPU→GPU GPU→GPU GPU→CPU

110 2016/01/13

CUDA Fortranの利点の一つ

Page 111: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAでカーネルを作成するときの制限

GPGPU講習会111

利用するデータの場所の把握

データがホストメモリにあるかデバイスメモリにあるか

CPUにあるデータを基にGPUで計算を行う場合

GPUで計算した結果をCPUで確認する場合

転送を明示的に指定

カーネルの引数

値を渡すことができる

GPUのメモリを指すアドレス

CPUのメモリを指すアドレスも渡すことは可能

そのアドレスを基にホスト側のメモリを参照することは不可能

2016/01/13

Page 112: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

attributes(global) &subroutine add(a, x, b, y, c)

implicit nonereal :: a(N)real :: b(N)real :: c(N)real,value :: x,y !値渡しinteger :: ido i=1,N

c(i) = x*a(i) + y*b(i)end do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kernel

use cudaforimplicit nonereal,allocatable,device :: a(:)real,allocatable,device :: b(:)real,allocatable,device :: c(:)real,allocatable :: host_c(:)allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0allocate(host_c(N))

call add<<<1,1>>>(a,1.0,b,2.0,c)host_c = cprint *,sum(host_c)/N

deallocate(a)deallocate(b)deallocate(c)deallocate(host_c)

end program main

GPUプログラム(双方向コピー)

GPGPU講習会

vectoradd_1thread_copy.cuf112 2016/01/13

Page 113: GPGPU Seminar (GPGPU and CUDA Fortran)

CUDAでカーネルを作成するときの制限

GPGPU講習会113

x,yはCPU側のメモリに存在

値渡し

CPU→GPUへ値がコピーされる

Fortranはポインタ渡し

値渡しにする場合はvalue属性を付ける

attributes(global) &subroutine add(a, x, b, y, c)

implicit nonereal :: a(N)real :: b(N)real :: c(N)real,value :: x,y !値渡しinteger :: ido i=1,N

c(i) = x*a(i) + y*b(i)end do

end subroutine add:

program main:call add<<<1,1>>>(a,1.0,b,2.0,c):

end program main

2016/01/13

Page 114: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUで並列に処理を行うには

GPUは低性能の演算器(CUDAコア)を多数搭載

マルチスレッドで並列処理することで高い性能を達成

どのように並列処理を記述するか?

関数呼出の際に関数名と引数の間に<<<1,1>>>を付けた

GPUには数百から数千のCUDAコアが搭載されており,それらが協調して並列処理を実行

1スレッドが実行する処理を書くことでカーネルの作成を簡略化

並列処理の度合いはカーネル呼出の際に指定する

GPGPU講習会114 2016/01/13

Page 115: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUで並列に処理を行うには

配列サイズN=8 総スレッド数を8として並列実行する状況を想定

GPGPU講習会115

c(i)

a(i)

b(i)

+ + + + + + + +

i=   1   2    3   4    5   6    7    8

2016/01/13

Page 116: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUによる並列化の方針

doループをスレッドの数だけ分割

各スレッドが少量のデータを処理

i=1c(i) = a(i) + b(i)スレッド1

c(i)

a(i)

b(i)

+ + + +

スレッド

1スレッド

3スレッド

2スレッド

4

i=2c(i) = a(i) + b(i)スレッド2

i=3c(i) = a(i) + b(i)スレッド3

i=4c(i) = a(i) + b(i)スレッド4

スレッドの番号に応じて決定

1スレッドが実行する処理

GPGPU講習会116 2016/01/13

Page 117: GPGPU Seminar (GPGPU and CUDA Fortran)

カーネルの書き換え

1スレッドが実行する処理になるよう変更

1スレッドがある添字 i の要素を担当

integer,parameter :: N=8

attributes(global) subroutine add(a, b, c)real :: a(N)real :: b(N)real :: c(N)integer :: ii = ...c(i) = a(i) + b(i)

end subroutine add

GPGPU講習会117

1スレッドがあるiの担当となり,変数の初期化と足し算の計算を実行

2016/01/13

Page 118: GPGPU Seminar (GPGPU and CUDA Fortran)

カーネルの書き換え

1スレッドが実行する処理になるよう変更

どのようにiを決定するか?

integer,parameter :: N=8

attributes(global) subroutine add(a, b, c)real :: a(N)real :: b(N)real :: c(N)integer :: ii = 1c(i) = a(i) + b(i)

end subroutine add

全てのスレッドがi=1の要素を計算してしまう

GPGPU講習会118 2016/01/13

Page 119: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの並列化の階層

GPUのハードウェアの構成に対応させて並列性を管理

並列化の各階層における情報を利用

GPU

Streaming Multiprocessor

CUDA Core

ハードウェア構成

並列に実行する処理

スレッドの集まり

スレッド

並列化の階層

Grid

Block

Thread

CUDA

GPGPU講習会119 2016/01/13

Page 120: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの並列化の階層

グリッド-ブロック-スレッドの3階層

各階層の情報を参照できる変数 x,y,zを成分(component)にもつdim3派生型

グリッド(Grid) gridDim グリッド内にあるブロックの数

ブロック(Block) blockIdx ブロックに割り当てられた番号

blockDim ブロック内にあるスレッドの数

スレッド(Thread) threadIdx スレッドに割り当てられた番号

GPGPU講習会120 2016/01/13

Page 121: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads(Fermi世代以降)

<<< , >>>内の数字で表示される内容が変化

2016/01/13GPGPU講習会121

module cuf_kernelimplicit nonecontains

attributes(global) subroutine hello()print *,"gridDim'%'x",gridDim%x,"blockIdx'%'x",blockIdx%x,&

"blockDim'%'x",blockDim%x,"threadIdx'%'x",threadIdx%xend subroutine helloend module cuf_kernelprogram main

use cudaforuse cuf_kernelimplicit noneinteger :: statcall hello<<<2,4>>>()stat = cudaThreadSynchronize()

end program main

<<<>>>内の数字を変えると画面表示される内容が変わる<<<>>>内の数字とどのパラメータが対応しているかを確認

・・・

hellothreads.cuf

Page 122: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads

2016/01/13GPGPU講習会122

gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 4gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 4

Page 123: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるiを参照するには

CUDAでは並列化に階層がある

全体の領域(グリッド)をブロックに分割

ブロックの中をスレッドに分割

<<<2, 4>>>

ブロックの数 1ブロックあたりのスレッドの数

ブロックの数×1ブロックあたりのスレッドの数=総スレッド数2    × 4          = 8 

GPGPU講習会123

[block] [thread/block] [thread]

2016/01/13

Page 124: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるiを参照するには

N=8, <<<2, 4>>>で実行

c(i)

a(i)

b(i)

+ + + + + + + +

gridDim%x=2

blockIdx%x=1 blockIdx%x=2

blockDim%x=4blockDim%x=4threadIdx%x=

1    2   3    4 1    2   3    4

threadIdx%x=

GPGPU講習会124

i=   1    2   3    4   5    6   7    8

2016/01/13

Page 125: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるiを参照するには

N=8, <<<2, 4>>>で実行

c(i)

a(i)

b(i)

+ + + + + + + +

gridDim%x=2

blockIdx%x=1 blockIdx%x=2

blockDim%x=4blockDim%x=4threadIdx%x=

1    2   3    4 1    2   3    4

threadIdx%x=

GPGPU講習会125

i=   1    2   3    4   5    6   7    8

2016/01/13

=  (blockIdx%x‐1)*blockDim%x + threadIdx%x

Page 126: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるiを参照するには

N=8, <<<1, 8>>>で実行

+ + + + + + + +

gridDim%x=1

blockIdx%x=1

blockDim%x=8threadIdx%x=

1    2   3    4 5    6   7    8

=  (blockIdx%x‐1)*blockDim%x + threadIdx%x

2016/01/13GPGPU講習会126

c(i)

a(i)

b(i)

i=   1    2   3    4   5    6   7    8

Page 127: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるiを参照するには

N=8, <<<4, 2>>>で実行

+ + + + + + + +

gridDim%x=4

blockIdx%x=1

blockDim%x=2threadIdx%x=

1 2   1    2 1    2   1    2

2016/01/13GPGPU講習会127

blockIdx%x=2

blockDim%x=2

blockIdx%x=3

blockDim%x=2

blockIdx%x=4

blockDim%x=2

=  (blockIdx%x‐1)*blockDim%x + threadIdx%x

c(i)

a(i)

b(i)

i=   1    2   3    4   5    6   7    8

Page 128: GPGPU Seminar (GPGPU and CUDA Fortran)

カーネルの書き換え

1スレッドが実行する処理になるよう変更

1スレッドがある添字 i の要素を担当

integer,parameter :: N=8

attributes(global) subroutine add(a, b, c)real :: a(N)real :: b(N)real :: c(N)integer :: ii = (blockIdx%x‐1)*blockDim%x + threadIdx%xc(i) = a(i) + b(i)

end subroutine add

GPGPU講習会128 2016/01/13

Page 129: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: N=2**20contains

attributes(global)&subroutine add(a, b, c)

implicit none

real :: a(N)real :: b(N)real :: c(N)integer :: ii = (blockIdx%x‐1)*blockDim%x&

+ threadIdx%xc(i) = a(i) + b(i)

end subroutine addend module vectoradd_kernel

program main

use vectoradd_kerneluse cudaforimplicit none

real,allocatable,device :: a(:)real,allocatable,device :: b(:)real,allocatable,device :: c(:)

allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add<<<N/256,256>>>(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

GPUで並列実行するプログラム

GPGPU講習会

vectoradd.cuf

129 2016/01/13

Page 130: GPGPU Seminar (GPGPU and CUDA Fortran)

処理時間の比較

配列の要素数 N=220

1ブロックあたりのスレッド数 256

GPUはマルチスレッドで処理しないと遅い GPUを使えばどのような問題でも速くなるわけではない

並列に処理できるようプログラムを作成する必要がある

implementation Processing time [ms]

CPU (1 Thread) 4.55

GPU (1 Thread) 206

GPU (256 Threads) 0.115

GPGPU講習会130 2016/01/13

Page 131: GPGPU Seminar (GPGPU and CUDA Fortran)

各階層の値の設定

設定の条件

GPUの世代によって設定できる上限値が変化

確認の方法

pgaccelinfo deviceQuery

GPU Computing SDKに含まれているサンプル

CUDA Programming Guide https://docs.nvidia.com/cuda/cuda‐c‐programming‐guide/#compute‐capabilities

階層の値によって実行時の性能が変化

GPUの一番基本的なチューニング

2016/01/13GPGPU講習会131

Page 132: GPGPU Seminar (GPGPU and CUDA Fortran)

pgaccelinfoの実行結果Device Number:                 0Device Name:                   Tesla M2050Device Revision Number:        2.0Global Memory Size:            2817982464Number of Multiprocessors:     14Number of Cores:               448Concurrent Copy and Execution: YesTotal Constant Memory:         65536Total Shared Memory per Block: 49152Registers per Block:           32768Warp Size:                     32Maximum Threads per Block:     1024Maximum Block Dimensions:      1024, 1024, 64Maximum Grid Dimensions:       65535 x 65535 x 65535Maximum Memory Pitch:          2147483647BTexture Alignment:             512BClock Rate:                    1147 MHzInitialization time:           4222411 microsecondsCurrent free memory:           2746736640Upload time (4MB):             2175 microseconds ( 829 ms pinned)Download time:                 2062 microseconds ( 774 ms pinned)Upload bandwidth:              1928 MB/sec (5059 MB/sec pinned)Download bandwidth:            2034 MB/sec (5418 MB/sec pinned)

2016/01/13GPGPU講習会132

Page 133: GPGPU Seminar (GPGPU and CUDA Fortran)

pgaccelinfo実行結果 Revision Number:       2.0

Global Memory Size:            2817982464

Warp Size:                     32

Maximum Threads per Block:     1024

Maximum Block Dimensions:      1024, 1024, 64

Maximum Grid Dimensions:       65535 x 65535 x 65535

GPUの世代(どのような機能を有しているか)

実行時のパラメータ

選択の際に重要

各方向の 大値

1ブロックあたりのスレッド数は 大1024 (1024, 1, 1), (1, 1024, 1)

(32, 32, 1), (4, 4, 64)など

2016/01/13GPGPU講習会133

Page 134: GPGPU Seminar (GPGPU and CUDA Fortran)

NB,NTをparameterとして定義,変更して実行module vectoradd_kernel

implicit noneinteger,parameter :: N=2**20integer,parameter :: NT=256integer,parameter :: NB=(N/NT)contains

attributes(global) &subroutine add(a, b, c)

implicit nonereal :: a(N)real :: b(N)real :: c(N)integer :: ii = (blockIdx%x‐1)*blockDim%x &

+ threadIdx%xc(i) = a(i) + b(i)

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kerneluse cudaforimplicit nonereal,allocatable,device :: a(:)real,allocatable,device :: b(:)real,allocatable,device :: c(:)allocate(a(N)); a=1.0allocate(b(N)); b=2.0allocate(c(N)); c=0.0

call add<<<NB,NT>>>(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

並列度の変更によるチューニング

GPGPU講習会134

vectoradd_param.cu

2016/01/13

Page 135: GPGPU Seminar (GPGPU and CUDA Fortran)

性能比較

2016/01/13GPGPU講習会135

1ブロックあたりのスレッド数が増加すると性能が向上

スレッド数を多くしすぎると性能が低下

CUDA CとCUDA Fortranで同じ傾向を示す

スレッド数が多いほど処理時間は短いが,多すぎると遅くなる

Number of Threads/Block

Processing time [ms]C Fortran

32 0.276 0.28064 0.169 0.170128 0.128 0.130256 0.119 0.116512 0.120 0.1171024 0.151 0.146

Page 136: GPGPU Seminar (GPGPU and CUDA Fortran)

ベクトル和C=A+B(2次元版)

2016/01/13GPGPU講習会136

1次元のベクトル和とほぼ同じ

並列化が容易

do文の書く順番で実行速度が変化

c(:,:) a(:,:) b(:,:)

Page 137: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: Nx=2**10integer,parameter :: Ny=2**10contains

subroutine add(a, b, c)implicit none

real :: a(Nx,Ny)real :: b(Nx,Ny)real :: c(Nx,Ny)

integer :: i,j//iとjのループを入れ替えるとどうなるか?do j=1,Nydo i=1,Nx

c(i,j) = a(i,j) + b(i,j)end doend do

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kernelimplicit none

real :: a(Nx,Ny)real :: b(Nx,Ny)real :: c(Nx,Ny)

a=1.0b=2.0c=0.0

call add(a,b,c)

print *,sum(c)/(Nx*Ny)end program main

ベクトル和(2次元版)

2016/01/13GPGPU講習会137

vectoradd2d.f90

Page 138: GPGPU Seminar (GPGPU and CUDA Fortran)

2次元配列の1次元配列表現

2016/01/13GPGPU講習会138

アドレス空間は1次元

2次元配列でも1次元のアドレスで表現

1次元目が連続か,2次元目が連続かが言語によって異なる

Fortranは1次元目が連続

j

i

j

i1次元目が連続

a(Nx,Ny) a(Nx,Ny)

2次元目が連続

Page 139: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUによる2次元的な並列処理

2016/01/13GPGPU講習会139

ブロックとスレッドを2次元的に設定

1スレッドが配列の1要素(画像の1ピクセル)を処理

スレッドを2次元的に配置してブロックを構成

Page 140: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの並列化の階層

グリッド-ブロック-スレッドの3階層

各階層の情報を参照できる変数 x,y,zを成分(component)にもつdim3派生型

グリッド(Grid) gridDim グリッド内にあるブロックの数

ブロック(Block) blockIdx ブロックに割り当てられた番号

blockDim ブロック内にあるスレッドの数

スレッド(Thread) threadIdx スレッドに割り当てられた番号

GPGPU講習会140 2016/01/13

Page 141: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads(2次元版)

<<< , >>>の中にどのように数字を書くか

2016/01/13GPGPU講習会141

module cuf_kernelimplicit nonecontains

attributes(global) subroutine hello()print *,"gridDim'%'x=", gridDim%x, "blockIdx'%'x=",  blockIdx%x,&

"blockDim'%'x=",blockDim%x,"threadIdx'%'x=", threadIdx%xprint *,"gridDim'%'y=", gridDim%y, "blockIdx'%'y=",  blockIdx%y,&

"blockDim'%'y=",blockDim%y,"threadIdx'%'y=", threadIdx%yend subroutine helloend module cuf_kernelprogram main

use cudaforuse cuf_kernelimplicit noneinteger :: statcall hello<<<?,?>>>()stat = cudaThreadSynchronize()

end program mainhellothreads2d.cu

Page 142: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads(2次元版)

1次元と同様<<<2,4>>>等として実行

実行結果(画面出力)

2016/01/13GPGPU講習会142

gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 4gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 2 blockDim'%'x= 4 threadIdx'%'x= 4gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1gridDim'%'y= 1 blockIdx'%'y= 1 blockDim'%'y= 1 threadIdx'%'y= 1

並列度の指定が

できていない

Page 143: GPGPU Seminar (GPGPU and CUDA Fortran)

2次元的な並列度の指定

2016/01/13GPGPU講習会143

<<<,>>>の中にどのように数字を書くか

1次元の場合は数字を書くことができた

2次元,3次元は数字を並べて書くことができない

dim3派生型を利用

type(dim3)  block=dim3(2,4,1)type(dim3) thread=dim3(4,2,1);call hello<<<block, thread>>>()

call hello<<<dim3(2,4,1), dim3(4,2,1)>>>()

dim3型変数block, threadを利用

・・・

あるいは直接dim3派生型として記述・・・

Page 144: GPGPU Seminar (GPGPU and CUDA Fortran)

Hello Threads(2次元版)

実行結果(画面出力)

2016/01/13GPGPU講習会144

gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 4gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 1gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 2gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 3gridDim'%'x= 2 blockIdx'%'x= 1 blockDim'%'x= 4 threadIdx'%'x= 4:(中略)gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 1gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 1gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 1gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 1gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 2gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 2gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 2gridDim'%'y= 4 blockIdx'%'y= 3 blockDim'%'y= 2 threadIdx'%'y= 2:

対応

対応

Page 145: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるi,jを参照するには

CUDAでは並列化に階層がある

全体の領域(グリッド)をブロックに分割

ブロックの中をスレッドに分割

<<<dim3(2,4,1), dim3(4,2,1)>>>

ブロックの数 1ブロックあたりのスレッドの数

x方向ブロック数×1ブロックあたりのスレッドの数×y方向ブロック数×1ブロックあたりのスレッドの数=総スレッド数

GPGPU講習会145 2016/01/13

総スレッド数=2×4×4×2=64

Page 146: GPGPU Seminar (GPGPU and CUDA Fortran)

各スレッドが異なるi,jを参照するには

2016/01/13GPGPU講習会146

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2 gridDim%x=2, gridDim%y=2 blockDim%x=4,blockDim%y=4

(1,1)(2,1)(3,1)(4,1)(1,1)

(4,4) (4,4)

(1,2)(2,2)(3,2)(4,2)

(1,3)(2,3)(3,3)(4,3)

(1,4)(2,4)(3,4)(4,4) (4,4)

(1,1) (1,1)

threadIdx%x,threadIdx%y

i= 1 2  3 4  5 6 7  8j=

12  3

4  56

7  8・・・

・・・

・・・・・・

・・・

・・・

Page 147: GPGPU Seminar (GPGPU and CUDA Fortran)

(1,1)(2,1)(3,1)(4,1)(1,1)

(4,4) (4,4)

(1,2)(2,2)(3,2)(4,2)

(1,3)(2,3)(3,3)(4,3)

(1,4)(2,4)(3,4)(4,4) (4,4)

(1,1) (1,1)

・・・

・・・

・・・・・・

・・・

・・・

各スレッドが異なるi,jを参照するには

2016/01/13GPGPU講習会147

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2

blockDim%x=4

blockDim%y=4

threadIdx%x,threadIdx%y

blockIdx%x=1 blockIdx%x=2

blockIdx%y=1

blockIdx%y=2

gridDim%x=2gridDim.y=2

Page 148: GPGPU Seminar (GPGPU and CUDA Fortran)

(1,1)(2,1)(3,1)(4,1)(1,1)

(4,4) (4,4)

(1,2)(2,2)(3,2)(4,2)

(1,3)(2,3)(3,3)(4,3)

(1,4)(2,4)(3,4)(4,4) (4,4)

(1,1) (1,1)

・・・

・・・

・・・・・・

・・・

・・・

各スレッドが異なるi,jを参照するには

2016/01/13GPGPU講習会148

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2 i = (blockIdx%x‐1)*blockDim%x + threadIdx%x j = (blockIdx%y‐1)*blockDim%y + threadIdx%y

block(1,1) block(2,1)

block(1,2) block(2,2)

threadIdx%x,threadIdx%y

i= 1 2  3 4  5 6 7  8j=

12  3

4  56

7  8

Page 149: GPGPU Seminar (GPGPU and CUDA Fortran)

module vectoradd_kernelimplicit noneinteger,parameter :: Nx=2**10integer,parameter :: Ny=2**10integer,parameter :: NTx=16integer,parameter :: NTy=16integer,parameter :: NBx=(Nx/NTx)integer,parameter :: NBy=(Ny/NTy)contains

attributes(global) subroutine add(a, b, c)implicit none

real :: a(Nx,Ny)real :: b(Nx,Ny)real :: c(Nx,Ny)integer :: i,ji=(blockIdx%x‐1)*blockDim%x+threadIdx%xj=(blockIdx%y‐1)*blockDim%y+threadIdx%yc(i,j) = a(i,j) + b(i,j)

end subroutine addend module vectoradd_kernel

program mainuse vectoradd_kerneluse cudaforimplicit nonereal,allocatable,device :: a(:,:)real,allocatable,device :: b(:,:)real,allocatable,device :: c(:,:)type(dim3) :: thread=dim3(NTx,NTy,1)type(dim3) ::  block=dim3(NBx,NBy,1)allocate(a(Nx,Ny)); a=1.0allocate(b(Nx,Ny)); b=2.0allocate(c(Nx,Ny)); c=0.0

call add<<<block, thread>>>(a,b,c)

deallocate(a)deallocate(b)deallocate(c)

end program main

ベクトル和(2次元並列版)

2016/01/13GPGPU講習会149

vectoradd2d.cuf

Page 150: GPGPU Seminar (GPGPU and CUDA Fortran)

実行結果

GPGPU講習会150

2次元版

カーネル スレッド数 実行時間[s]add 16×16(=256) 155

2016/01/13

# CUDA_PROFILE_LOG_VERSION 2.0# CUDA_DEVICE 0 Tesla M2050# TIMESTAMPFACTOR fffff5f14d408b28method,gputime,cputime,occupancy:method=[ add ] gputime=[ 154.976 ] cputime=[ 6.000 ] occupancy=[ 1.000 ]

Page 151: GPGPU Seminar (GPGPU and CUDA Fortran)

実行結果

GPGPU講習会151

1次元版(vectoradd.cuf)

カーネル スレッド数 実行時間[s]add 256 113

# CUDA_PROFILE_LOG_VERSION 2.0# CUDA_DEVICE 0 Tesla M2050# TIMESTAMPFACTOR fffff5f0d1572620method,gputime,cputime,occupancy:method=[ add ] gputime=[ 114.816 ] cputime=[ 6.000 ] occupancy=[ 1.000 ]

2016/01/13

Page 152: GPGPU Seminar (GPGPU and CUDA Fortran)

ベクトル和(2次元並列版)の実行結果

GPGPU講習会152

実行結果

1次元版と比較して実行時間がかかる

遅くなる要因は?

2次元は添字iだけでなくjも計算

添字の計算負荷は軽い

実行時の並列度の指定

2016/01/13

Page 153: GPGPU Seminar (GPGPU and CUDA Fortran)

ベクトル和(2次元並列版)の実行結果

GPGPU講習会153

x,y方向スレッド数による実行時間の変化

1ブロックあたりのスレッド数を256に固定

2016/01/13

スレッド数 実行時間[s]1×256 11362×128 6014× 64 3418× 32 213

16× 16 15532× 8 11964× 4 135

128× 2 121256× 1 116

1次元の場合とほぼ同じ

Page 154: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUのメモリ階層

長岡技術科学大学 電気電子情報工学専攻 出川智啓

Page 155: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUの主要部品

GPGPU講習会

基盤

画面出力端子

電源入力端子

GPU(チップ)+冷却部品

メモリ

特性の把握が重要

http://www.geforce.com/whats‐new/articles /introducing‐the‐geforce‐gtx‐780に公開されている写真を基に作成

画面出力端子

PCI‐Ex端子

電源入力端子

メモリ

チップ

155 2016/01/13

Page 156: GPGPU Seminar (GPGPU and CUDA Fortran)

CPUのメモリ階層

GPGPU講習会

オフチップ(off‐chip)メモリ

CPUのチップ外部に置かれたメモリ

メインメモリ(主記憶)

利用可能なメモリの中で 低速, 大容量

オンチップ(on‐chip)メモリ

CPUのチップ内部に置かれたメモリ

レジスタ

レベル1(L1)キャッシュ

レベル2(L2)キャッシュ

レベル2(L3)キャッシュ

156

高速,容量小

低速,容量大

2016/01/13

Page 157: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUのメモリ階層

GPGPU講習会

オフチップメモリ

PCI‐Exカードの基板上に実装

ビデオメモリ

利用可能なメモリの中で 低速, 大容量

オンチップメモリ

GPUのチップ内部に置かれたメモリ

レジスタ

レベル1(L1)キャッシュ

レベル2(L2)キャッシュ

CPUの構造に類似

157

高速,容量小

低速,容量大

2016/01/13

Page 158: GPGPU Seminar (GPGPU and CUDA Fortran)

GPUメモリの独自の特徴

GPGPU講習会

CPUとは異なるメモリを複数搭載

各メモリの特徴を知り,適材適所で利用する事により高速化

GPUから読み書き可能か

処理を行うスレッドから読み書き可能か,読み込みのみか

複数のスレッドでデータを共有できるか

CPUから読み書き可能か

C言語の機能のみで直接読み書きは不可能

CUDAの専用関数(API)を利用して読み書き

158 2016/01/13

Page 159: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの階層

GPGPU講習会

CPUのメモリ階層

コアごとにL2キャッシュ,全体でL3キャッシュを持つこともある

メインメモリ

L2キャッシュ

・・・

・・・

チップ

159

コア

L1キャッシュ

演算器

レジスタ レジスタ

演算器

L1キャッシュ

演算器

レジスタ レジスタ

演算器

L1キャッシュ

演算器

レジスタ レジスタ

演算器

コア コア

2016/01/13

Page 160: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの階層

GPGPU講習会

GPUのメモリ階層

CPUにはない独自のメモリを複数持つ

グローバルメモリ

L2キャッシュ

L1キャッシュ 共有メモリ

CUDA Core

レジスタ

チップ

テクスチャメモリ

コンスタントメモリ

ローカルメモリ

テクスチャキャッシュ

コンスタントキャッシュ

160 2016/01/13

Page 161: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの種類

オンチップメモリ(GPUのチップ内部に置かれたメモリ)

高速アクセス,小容量

CPUからはアクセス不可

L1キャッシュと共有メモリは一定サイズを共用

GPGPU講習会161

L1キャッシュ/共有(シェアード)メモリ

レジスタ

容量 小 小

速度 高速 高速

GPUからの読み書き

読み書き可ブロック内の全スレッドが同じメモリにアクセス(データを共有する)ことが可

能*

読み書き可各スレッドが異なるアドレス

にアクセス

CPUからのアクセス

読み書き不可 読み書き不可

2016/01/13

*同じメモリ,異

なるメモリについては後ろのスライドで説明

Page 162: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの種類

オフチップメモリ(GPUのチップ外部に置かれたメモリ)

低速アクセス,大容量

CPUから直接アクセス可能

ローカルメモリだけはアクセス不可

GPGPU講習会162

グローバルメモリ ローカルメモリ テクスチャメモリ コンスタントメモリ

容量 大 小 大 小

速度 低速 低速 高速*1 高速*1

GPUからの読み書き

読み書き可全てのスレッドが同じメモリにアクセス可能*2

読み書き可各スレッドが異なるメモリにアクセス*2

読み込み可全てのスレッドが同じメモリにアクセス可能*2

読み込み可全てのスレッドが同じメモリにアクセス*2

CPUからのアクセス

読み書き可 読み書き不可 読み書き可 読み書き可

2016/01/13

*1キャッシュが効く場合*2同じメモリ,異なるメモリについては後ろのスライドで説明

Page 163: GPGPU Seminar (GPGPU and CUDA Fortran)

同じメモリ

2016/01/13GPGPU講習会163

複数のスレッドがメモリアドレスを共有

複数のスレッドが変数を共有

他のスレッドが書き込んだデータを読むことが可能

共有できるスレッドの範囲はメモリの種類によって変化

!a,b()がグローバルメモリに確保されている場合*1

attributes(global) subroutine kernel(...)integer ii=blockIdx%x*blockDim%x + threadIdx%xb(i) = i !スレッドiが配列bのi番目に自身のスレッド番号を代入

!b(1,2,...,i‐1,i,...)の値は1,2,...,i‐1,i,...a = b(i‐1)*2 !スレッドi‐1が書き込んだ値を読み*3,aに代入:        ! 後に書き込まれたaの値を全スレッドが共有

end subroutine *1 あくまで動作のイメージを説明するための例で正しく実行できない*2 iが1の場合は考えない*3 配列bを全スレッドが共有しているので,書き込んだ値以外を読む事が可能

Page 164: GPGPU Seminar (GPGPU and CUDA Fortran)

異なるメモリ

2016/01/13GPGPU講習会164

メモリアドレスが共有できず,一つのスレッドのみがそのメモリアドレスにアクセス

あるスレッドが宣言した変数へアクセスできない

!a,b()がレジスタに確保されている場合*1

attributes(global) subroutine kernel(...)integer ii=blockIdx%x*blockDim%x + threadIdx%xb(i) = i !スレッドiが配列bのi番目に自身のスレッド番号を代入

!b(1,2,...,i‐1,i,...)の値はb(i)以外不定a = b(i‐1)*2 !b(i‐1)の値(不定)をaに代入*3

:         !aの値はスレッドによって異なるend subroutine *1 あくまで動作のイメージを説明するための例で正しく実行できない

*2 iが1の場合は考えない*3 配列bは他のスレッドからアクセスできないため,代入した値以外は不定のまま

Page 165: GPGPU Seminar (GPGPU and CUDA Fortran)

Tesla世代

L1,L2キャッシュなし

テクスチャキャッシュ,コンスタントキャッシュは利用可能

高速なメモリの活用が重要

共有メモリ

レジスタ

メモリの種類

2016/01/13GPGPU講習会165

オフチップメモリ

オンチップメモリホストメモリ

コンスタントメモリ

テクスチャメモリ

GPUChip

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

共有メモリ

SM

グローバルメモリ ローカルメモリ

ローカルメモリ ・・・

Page 166: GPGPU Seminar (GPGPU and CUDA Fortran)

Fermi世代以降

共有メモリとL1キャッシュが一体で利用可能

グローバルメモリへのアクセスはL2キャッシュ経由

Fermi世代では標準でL1キャッシュも有効化*

メモリの種類

2016/01/13GPGPU講習会166

オフチップメモリ

オンチップメモリホストメモリ

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPUChip

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

グローバルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

*Kepler世代では標準でL1キャッシュが無効化

Page 167: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの種類と並列化階層の対応

オンチップメモリ

ブロックまたはスレッドごとに異なる値を持つ

ローカルメモリはレジスタが不足した時に使われる

オフチップメモリ

GPU全体で共通の値を持つ

2016/01/13GPGPU講習会167

各GPU(Grid)内でデータを共有

各ブロック内でデータを共有

各スレッドが個別のデータを保有

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

Grid

レジスタ

レジスタ

レジスタ

レジスタ

Thread 0

Thread 1

Thread 2

Thread 3

L1キャッシュ

共有メモリ

Block(0,0,0)

レジスタ

レジスタ

レジスタ

レジスタ

Thread 0

Thread 1

Thread 2

Thread 3

L1キャッシュ

共有メモリ

Block(1,0,0)

グローバルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

ホストメモリ

Page 168: GPGPU Seminar (GPGPU and CUDA Fortran)

レジスタ

各スレッドが個別に利用

カーネル内で変数を宣言するとレジスタを利用

非常に高速

キャッシュとしても利用可能

2016/01/13GPGPU講習会168*Keplerからは65536本

少容量 32768本*×32bit 利用可能分を超えるとローカルメモリへ追い出される

レジスタスピル

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPU

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

グローバルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

Chip

ホストメモリ

Page 169: GPGPU Seminar (GPGPU and CUDA Fortran)

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPU

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

グローバルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

グローバルメモリ

ビデオメモリ(数GB) CPUのメインメモリに相当

読み込みがある一定サイズでまとめて行われる

レイテンシが大きい

効率よくアクセスするための条件がある

コアレスアクセス

アラインアクセス

2016/01/13GPGPU講習会169

Tesla世代ではコアレスアクセスの条件にメモリのアラインが含まれている

Chip

ホストメモリ

Page 170: GPGPU Seminar (GPGPU and CUDA Fortran)

グローバルメモリへのアクセス(Tesla世代)

16スレッドが協調して同時にアクセス

1 Warp = 32スレッド

Warpの半分(Half Warp,16スレッド)が協調して処理を実行

コアレスアクセスか否かで読み込みの速度が変化

新しい世代のGPUでは,コアレスアクセスになる条件やコアレスアクセスできないときの速度の落ち込みが緩和

コアレスアクセスはGPUのプログラムで 重要

GPUの処理能力と比較するとデータ供給が不足しがち

極力データ供給不足が生じないようプログラムを作成

2016/01/13GPGPU講習会170

Page 171: GPGPU Seminar (GPGPU and CUDA Fortran)

コアレスアクセスになる条件(Tesla世代)

データのサイズ

32bit, 64bit, 128bit(4byte, 8byte, 16byte)

アドレスの隣接

16スレッドがアクセスするアドレスがスレッド番号順に隣接

スレッド番号順にデータサイズ分ずつアドレスが増加

アクセスする 初のアドレス

16スレッドがアクセスするアドレスの先頭が,64byteまたは128byteの倍数

2016/01/13GPGPU講習会171

Page 172: GPGPU Seminar (GPGPU and CUDA Fortran)

コアレスアクセスの例*

(Tesla世代)

データ型が32bit=4byte

各スレッドが連続して隣接アドレスにアクセス

先頭アドレスが128バイト境界

16スレッド

データ型が32bit=4byte

各スレッドが連続して隣接アドレスにアクセス

実際にデータを取得するかは無関係

先頭アドレスが128バイト境界

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

2016/01/13GPGPU講習会172

*CUDA Programming Guide

メモリアドレスに対するスレッドのアクセス要求

Page 173: GPGPU Seminar (GPGPU and CUDA Fortran)

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

コアレスアクセスにならない例(Tesla世代)

2016/01/13GPGPU講習会173

・・・

各スレッドが番号順にアクセス

していない

先頭が128バイト境界ではない

アドレスが連続していない

データが32bit, 64bit, 128bit

ではない

Page 174: GPGPU Seminar (GPGPU and CUDA Fortran)

コアレスアクセスにならない例(Tesla世代)

2016/01/13GPGPU講習会174

128バイト境界からわずかにずれている場合

Tesla世代以降は64バイトブロックと32バイトブロックに分けて読込

メモリアクセス要求は2回

コアレスアクセスの半分程度の性能は得られる

A190

64バイトブロック

でデータ読込

32バイトブロック

でデータ読込

T15

T14

T13

T12

T11

T10

T9

T8

T7

T6

T5

T4

T3

T2

A188

A184

A180

A176

A172

A168

A164

A160

A156

A152

A148

A144

A140

A136

T1 A132

T0 A128

Page 175: GPGPU Seminar (GPGPU and CUDA Fortran)

グローバルメモリへのアクセス(Fermi世代)

2016/01/13GPGPU講習会175

Tesla世代のGPU グローバルメモリへのアクセスに強い制約があった

以前のGPUよりは改善

Fermi世代以降のGPU Tesla世代のGPUから大幅に改善

効率のよいアクセスの条件

コアレスアクセス+アラインアクセス

L1キャッシュの利用状況によっても変化

Page 176: GPGPU Seminar (GPGPU and CUDA Fortran)

グローバルメモリからの読込(Fermi世代)

2016/01/13GPGPU講習会176

メモリ操作の命令はWarpごとに発行

1 Warp内の32スレッドが協調

各スレッドのメモリアクセス要求を一つに集約

メモリアクセス要求の処理

128バイトもしくは32バイト単位

すべてL2キャッシュを通過

アーキテクチャによってはL1キャッシュも通過

L1キャッシュの使用はコンパイルオプションで設定可能

Fermi世代は標準でL1キャッシュが有効化

Kepler世代は標準でL1キャッシュが無効化

Page 177: GPGPU Seminar (GPGPU and CUDA Fortran)

グローバルメモリからの読込(Fermi世代)

2016/01/13GPGPU講習会177

コアレスメモリアクセス(coalesce access) メモリアドレスの連続性に着目

1 Warp内の32スレッドが隣り合ったメモリにアクセス

データサイズが4バイトの時,スレッド1がアクセスするメモリアドレスはスレッド0がアクセスするメモリアドレス+4

アラインメモリアクセス(align access) メモリの配置に着目

Warpがアクセスするデータの先頭アドレスがキャッシュ粒度の倍数

L2キャッシュのみを使う場合は32バイトの倍数

L1キャッシュも使う場合は128バイトの倍数

Page 178: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリ読込の例

2016/01/13GPGPU講習会178

アライン/コアレスアクセス

ミスアライン/コアレスアクセス

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

012345678910111213141516171819202122232425262728293031

メモリアドレスに対するスレッドのアクセス要求

Warp内でのスレッドID(カーネル内でのスレッド番号とは異なる)

メモリアドレス

Page 179: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリ読込の例

2016/01/13GPGPU講習会179

アライン/アンコアレスアクセス

ミスアライン/アンコアレスアクセス

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

012345678910111213141516171819202122232425262728293031

メモリアドレスに対するスレッドのアクセス要求

Page 180: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされる読込

2016/01/13GPGPU講習会180

L1キャッシュとL2キャッシュを通過

読込は128バイト単位で実行

L1キャッシュのキャッシュラインのサイズで実行

読込の評価に利用する用語

メモリトランザクション

メモリのアクセス要求に対して排他的にメモリにアクセスして行う処理の単位

バスの利用率

必要なデータサイズ/読込完了に必要な読込データサイズ

Page 181: GPGPU Seminar (GPGPU and CUDA Fortran)

アライン/コアレスアクセス

Warp内の全スレッドが要求するメモリアドレスが128バイトの範囲内

全スレッドが隣り合ったメモリにアクセス

先頭データのメモリアドレスが128バイトの倍数

読込に必要な128バイトのトランザクションは一つ

読み込んだデータ全てを利用

バスの利用率は128/128=100%

0 16

32

48

64

80

96

112

キャッシュされる読込の例

2016/01/13GPGPU講習会181

128

144

160

176

192

208

224

240

012345678910111213141516171819202122232425262728293031

256

272

288

304

320

336

352

358

Warp内でのスレッドID(カーネル内でのスレッド番号とは異なる)

メモリアドレス

メモリアドレスに対するスレッドのアクセス要求

読込に必要な128バイトトランザクション

128バイトのキャッシュライン読み込むデータ

Page 182: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされる読込の例

2016/01/13GPGPU講習会182

アライン/アンコアレスアクセス

Warp内の全スレッドが要求するメモリアドレスが128バイトの範囲内(一つのキャッシュラインに収まる)

各スレッドがアクセスするメモリアドレスが不連続

先頭データのメモリアドレスが128バイトの倍数

読込に必要な128バイトのトランザクションは一つ

読み込んだデータ全てを利用

バスの利用率は128/128=100%

0 16

32

48

64

80

96

112

012345678910111213141516171819202122232425262728293031

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 183: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされる読込の例

2016/01/13GPGPU講習会183

ミスアライン/コアレスアクセス

Warpがアクセスするデータの先頭アドレスが128の倍数ではない(一つのキャッシュラインに収まらない)

全スレッドが隣り合ったメモリにアクセス

先頭データのメモリアドレスが128バイトの倍数ではない

読込に必要な128バイトのトランザクションは二つ

読み込んだデータの半分だけを利用

バスの利用率は128/(128×2)=50%

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 184: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされる読込の例

2016/01/13GPGPU講習会184

ミスアライン/コアレスアクセス

Warp内の全スレッドが同じアドレスにアクセス要求

一つのキャッシュラインに収まる

読込に必要な128バイトのトランザクションは一つ

読み込んだ128バイトのうち利用されるのは4バイトのみ

バスの利用率は4/128=3.125%

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 185: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされる読込の例

2016/01/13GPGPU講習会185

ミスアライン/コアレスアクセス

Warp内の各スレッドが広範囲に点在するデータにアクセス

複数のキャッシュラインにまたがる(上の例では3個)

読込に必要な128バイトのトランザクションは 悪で32個 読み込んだデータのうち128バイトのみを利用

バスの利用率は 悪で128/(128×32)=3.125% 上の例では128/(128×3)=33.3%

何度も読み込むための待ちが発生

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 186: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされない読込

2016/01/13GPGPU講習会186

L1キャッシュを無効化

L2キャッシュのみを通過

読込は32バイト単位で実行

メモリセグメントのサイズで実行

細かい単位で実行されるため,ミスアライン・アンコアレスメモリアクセスのパフォーマンスが改善される可能性がある

セグメント

メモリの管理方式の一つ

まとまった大きさで管理されたメモリ空間

Page 187: GPGPU Seminar (GPGPU and CUDA Fortran)

アライン/コアレスアクセス

Warp内の全スレッドが要求するメモリアドレスが128バイト(四つのセグメントに収まる)

全スレッドが隣り合ったメモリにアクセス

先頭データのメモリアドレスが32バイトの倍数

読込に必要な32バイトのトランザクションは四つ

読み込んだデータ全てを利用

バスの利用率は128/(32×4)=100%

キャッシュされない読込の例

2016/01/13GPGPU講習会187

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Warp内でのスレッドID(カーネル内でのスレッド番号とは異なる)

メモリアドレス

メモリアドレスに対するスレッドのアクセス要求

読込に必要な32バイトトランザクション

32バイトのメモリセグメント読み込むデータ

Page 188: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされない読込の例

2016/01/13GPGPU講習会188

アライン/アンコアレスアクセス

Warp内の全スレッドが要求するメモリアドレスが128バイト(四つのセグメントに収まる)

各スレッドがアクセスするメモリアドレスが不連続

先頭データのメモリアドレスが32バイトの倍数

読込に必要な32バイトのトランザクションは四つ

読み込んだデータ全てを利用

バスの利用率は128/(32×4)=100%

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 189: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされない読込の例

2016/01/13GPGPU講習会189

ミスアライン/コアレスアクセス

Warpがアクセスするデータの先頭アドレスが128の倍数ではない(五つのセグメントにまたがる)

全スレッドが隣り合ったメモリにアクセス

先頭データのメモリアドレスが32バイトの倍数ではない

読込に必要な32バイトのトランザクションは五つ

読み込んだデータの8割は利用

バスの利用率は128/(32×5)=80% 改善!

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 190: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされない読込の例

2016/01/13GPGPU講習会190

ミスアライン/アンコアレスアクセス

Warp内の全スレッドが同じアドレスにアクセス要求

一つのセグメントに収まる

読込に必要な32バイトのトランザクションは一つ

読み込んだ32バイトのうち利用されるのは4バイトのみ

バスの利用率は4/32=12.5% 改善!

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 191: GPGPU Seminar (GPGPU and CUDA Fortran)

キャッシュされない読込の例

2016/01/13GPGPU講習会191

ミスアライン/アンコアレスアクセス

Warp内の各スレッドが広範囲に点在するデータにアクセス

複数のセグメントにまたがる(上の例では10個)

読込に必要な32バイトのトランザクションは 悪で32個 悪でも32バイトのセグメント32個に分散

バスの利用率は 悪で128/(32×32)=12.5% 改善!

上の例では128/(32×10)=40% 改善!

何度も読み込むための待ちは依然として発生

012345678910111213141516171819202122232425262728293031

0 16

32

48

64

80

96

112

128

144

160

176

192

208

224

240

256

272

288

304

320

336

352

358

Page 192: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの種類

オンチップメモリ(GPUのチップ内部に置かれたメモリ)

高速アクセス,小容量

CPUからはアクセス不可

L1キャッシュと共有メモリは一定サイズを共用

GPGPU講習会192

L1キャッシュ/共有(シェアード)メモリ

レジスタ

容量 小 小

速度 高速 高速

GPUからの読み書き

読み書き可ブロック内の全スレッドが同じアドレスにアクセス(データを共有する)ことが

可能*

読み書き可各スレッドが異なるレジスタ

にアクセス

CPUからのアクセス

読み書き不可 読み書き不可

*スレッドごとに

異なるアドレスにアクセスすることも可能

2016/01/13

Page 193: GPGPU Seminar (GPGPU and CUDA Fortran)

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPU

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

グローバルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

Chip

共有(シェアード)メモリ

ブロック内のスレッドが共通のデータ(メモリアドレス)にアクセス可能

物理的にSMに近い

遅延はグローバルメモリの20~30分の1,帯域幅は10倍

2016/01/13GPGPU講習会193

Fermi世代以前のGPUでマネージドキャッシュとして利用

1ブロックあたり16,32*,48kB

*Kepler世代から

ホストメモリ

Page 194: GPGPU Seminar (GPGPU and CUDA Fortran)

共有(シェアード)メモリの宣言

カーネル内でshared属性を付けて宣言

配列として宣言

配列要素数を静的(コンパイル時)に決定する場合

型,shared 変数名(要素数) 多次元配列も宣言可能

配列要素数を動的(カーネル実行時)に決定する場合

型,shared 変数名(:) サイズはカーネル呼出時のパラメータで指定

<<<ブロック数,スレッド数,共有メモリのバイト数>>>

GPGPU講習会194 2016/01/13

=要素数*共有メモリの型のサイズ

Page 195: GPGPU Seminar (GPGPU and CUDA Fortran)

ブロック内でのスレッドの同期

GPGPU講習会

syncthreads() カーネル実行中にスレッドの同期を取る

syncthreads()が書かれた行にスレッドが到達すると,同一ブロック内の他の全てのスレッドがその行に達するまで待機

異なるブロック間での同期は不可能

ifの中に記述するとカーネルが終了しないことがある

195 2016/01/13

if(条件)thencall syncthreads();//条件が真にならないスレッドはifの中に入らないため,//カーネルが永久に終わらない

end if

Page 196: GPGPU Seminar (GPGPU and CUDA Fortran)

共有(シェアード)メモリ容量の選択

2016/01/13GPGPU講習会196

共有メモリとL1キャッシュは64kBを共用

どちらを多く利用するかを決定する関数が用意されている

64kB全ての利用は不可能

L1キャッシュ48kB, 共有メモリ16kB cudaDeviceSetCacheConfig(cudaFuncCachePreferL1)

L1キャッシュ16kB, 共有メモリ48kB cudaDeviceSetCacheConfig(cudaFuncCachePreferShared)

L1キャッシュ32kB/共有メモリ32kB* cudaDeviceSetCacheConfig(cudaFuncCachePreferEqual)

*Kepler世代から

Page 197: GPGPU Seminar (GPGPU and CUDA Fortran)

共有(シェアード)メモリへのアクセス(Tesla世代)

高速にアクセスするための制約が緩い

16スレッド(Half Warp)単位でアクセス

16個のバンクから構成 32bit=4バイトが1バンク

一つのバンクにアクセスできるのはHalf Warp内の1スレッドのみ 複数のスレッドが一つのバンクに同時にアクセスすると バンクコンフリクト(バンク衝突)が発生

共有メモリへのアクセスが逐次化される

2016/01/13GPGPU講習会197

Page 198: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトにならない例(Tesla世代)

Half Warpが異なるバンクにアクセス

隣接データにアクセス

データのサイズは32bit (float型)

コアレスアクセスに類似

Half Warpが異なるバンクにアクセス

ランダムアクセス

データのサイズは32bit

アドレスが不連続でもよい

コアレスアクセスと異なる

15

14

13

12

11

10

9

8

7

6

5

4

3

2

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1 1

0 0

15

14

13

12

11

10

9

8

7

6

5

4

3

2

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1 1

0 0

2016/01/13GPGPU講習会198

メモリアドレスに対するスレッドのアクセス要求 Half Warp内でのスレッドID

バンク番号

Page 199: GPGPU Seminar (GPGPU and CUDA Fortran)

96bit(12バイト)ごとにアクセス

float型データ3個分の構造体など

同じバンクにはアクセスしていない

124

120

116

112

108

104

100

96

92

88

84

80

76

72

68

64

188

184

180

176

172

164

160

160

156

152

148

144

140

136

132

128

バンクコンフリクトにならない例(Tesla世代)

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

60

56

52

48

44

40

36

32

28

24

20

16

12

8

4

0

2016/01/13GPGPU講習会199

メモリアドレスに対するスレッドのアクセス要求

メモリアドレス

0

23

5

1

6

8

14

4

7

9

15

10111213

バンク番号

Page 200: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトする例(Tesla世代)

64bit(8バイト)ごとにアクセス

double型では必ずバンクコンフリクトが発生

2wayバンクコンフリクト

スレッド0と8, 1と9,2と10...は同時に共有メモリにアクセスできない

124

120

116

112

108

104

100

96

92

88

84

80

76

72

68

64

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

60

56

52

48

44

40

36

32

28

24

20

16

12

8

4

0

2016/01/13GPGPU講習会200

メモリアドレスに対するスレッドのアクセス要求

0

23

5

1

6

8

14

4

7

9

15

10111213

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

バンク番号

Page 201: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトする例(Tesla世代)

256bit(32バイト)ごとにアクセス

8wayバンクコンフリクト

124

120

116

112

108

104

100

96

92

88

84

80

76

72

68

64

188

184

180

176

172

164

160

160

156

152

148

144

140

136

132

128

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

60

56

52

48

44

40

36

32

28

24

20

16

12

8

4

0

252

248

244

240

236

232

228

224

220

216

212

208

204

200

196

192

2016/01/13GPGPU講習会201

メモリアドレスに対するスレッドのアクセス要求

0

23

5

1

6

8

14

4

7

9

15

10111213

バンクコンフリクト

バンクコンフリクト

バンク番号

Page 202: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトしない例(Tesla世代)

16スレッドが一つの

バンクの同一アドレスにアクセス

配列のある要素のデータを全スレッドが共有

バンクコンフリクトは発生しない

ブロードキャストが行われる

60

56

52

48

44

40

36

32

28

24

20

16

12

8

4

0

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

2016/01/13GPGPU講習会202

メモリアドレスに対するスレッドのアクセス要求

0

23

5

1

6

8

14

4

7

9

15

10111213

バンク番号

Page 203: GPGPU Seminar (GPGPU and CUDA Fortran)

共有(シェアード)メモリへのアクセス(Fermi世代以降)

32スレッド(1 Warp)単位でアクセス

32個のバンクから構成 Compute Capabilityが2.x 32bit=4バイトが1バンク

帯域幅はクロックサイクルふたつあたり32ビット

Compute Capabilityが3.x 64bit=8バイトが1バンク 帯域幅はクロックサイクルあたり64ビット

バンクコンフリクトはFermiと同じか少ない

関数でバンクサイズを設定可能 cudaDeviceSetShareMemConfig(設定) 設定

cudaSharedMemBankSizeFourByte 4バイト

cudaSharedMemBankSizeEightByte 8バイト

2016/01/13GPGPU講習会203

Page 204: GPGPU Seminar (GPGPU and CUDA Fortran)

128

バンクコンフリクトにならないアクセス(Fermi世代)

2016/01/13GPGPU講習会204

Warp内の32スレッドが異なるバンクにある32ビットのデータに隣接アクセス

理想的なアクセスパターン

Warpが発行した共有メモリの読込,書込命令が一つのトランザクションで処理される

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

バンク番号 0 1 2 3

144

4 5 6 7

160

8 9 10 11

176

12 13 14 15

192

16 17 18 19

208

20 21 22 23

224

24 25 26 27

240

28 29 30 31

Page 205: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトにならないアクセス(Fermi世代)

2016/01/13GPGPU講習会205

Warp内の32スレッドが異なるバンクにある32ビットのデータにランダムアクセス

各スレッドは異なるバンクにアクセス

バンクコンフリクトは発生しない

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

128

0 1 2 3

144

4 5 6 7

160

8 9 10 11

176

12 13 14 15

192

16 17 18 19

208

20 21 22 23

224

24 25 26 27

240

28 29 30 31バンク番号

Page 206: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトにならないアクセス(Fermi世代)

2016/01/13GPGPU講習会206

複数のスレッドが一つのバンクの同一アドレスにアクセス

バンクコンフリクトは発生しない

メモリトランザクションが一つ実行され,アクセスした複数のスレッドに値がブロードキャストされる

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

128

0 1 2 3

144

4 5 6 7

160

8 9 10 11

176

12 13 14 15

192

16 17 18 19

208

20 21 22 23

224

24 25 26 27

240

28 29 30 31バンク番号

Page 207: GPGPU Seminar (GPGPU and CUDA Fortran)

バンクコンフリクトになるアクセス(Fermi世代)

2016/01/13GPGPU講習会207

複数のスレッドが一つのバンクの異なるアドレスにアクセス

バンクコンフリクトが発生(下の例では4‐way) メモリアクセスが逐次化( 悪で32倍の時間がかかる)

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

0

128

256

384

0 1 2 3

16

144

272

400

4 5 6 7

32

160

288

416

8 9 10 11

48

176

304

432

12 13 14 15

64

192

320

448

16 17 18 19

80

208

336

464

20 21 22 23

96

224

352

480

24 25 26 27

112

240

358

496

28 29 30 31バンク番号

バンクコンフリクト

バンクコンフリクト

バンクコンフリクト

Page 208: GPGPU Seminar (GPGPU and CUDA Fortran)

メモリの種類

オフチップメモリ(GPUのチップ外部に置かれたメモリ)

低速アクセス,大容量

CPUから直接アクセス可能

ローカルメモリだけはアクセス不可

GPGPU講習会208

グローバルメモリ ローカルメモリ テクスチャメモリ コンスタントメモリ

容量 大 小 大 小

速度 低速 低速 高速* 高速*

GPUからの読み書き

読み書き可全てのスレッドが同じメモリにアクセス

読み書き可各スレッドが異なるメモリにアクセス

読み込み可全てのスレッドが同じメモリにアクセス

読み込み可全てのスレッドが同じメモリにアクセス

CPUからのアクセス

読み書き可 読み書き不可 書き込み可 書き込み可

2016/01/13*キャッシュが効く場合

Page 209: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリ

GPU全体で同じメモリにアクセス

メモリを読み取り専用とすることで値をキャッシュし,一度読んだ値を再利用

2016/01/13GPGPU講習会

GPU全体で64kB

209

L2キャッシュ

コンスタントメモリ

テクスチャメモリ

GPUChip

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

レジスタ

レジスタ

レジスタ

レジスタ

CUDA Core

CUDA Core

CUDA Core

CUDA Core

L1キャッシュ

共有メモリ

SM

グローバルメモリ

ホストメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

ローカルメモリ

・・・

・・・

Page 210: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリの宣言

グローバル領域でconstant属性を付けて宣言

配列サイズは静的に決定

型,constant 変数名

型,constant 変数名(要素数) 配列としても宣言可能

サイズはコンパイル時に確定している必要がある

allocate/deallocateは不要

グローバル変数として宣言し,複数のカーネルからアクセスすることが多い

読込専用なので許される

書込可能なメモリでは厳禁

2016/01/13GPGPU講習会210

Page 211: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリの利用

メモリは読込専用

CPUからは変更可能

グローバルメモリと同様,代入演算子を用いてコピー

CUDA Fortranでは,コンスタントメモリをparameterと同じ感覚で利用できる

あるmodule内で変数を定義

moduleをuseしたカーネル内で読込

2016/01/13GPGPU講習会211

CUDA Fortranの利点の一つ

Page 212: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Tesla世代)

コンスタントメモリへ高速にアクセスできる要因

1. ブロードキャストによるデータの分配

16スレッド(Half Warp)単位でアクセスし,1回の読込を他のスレッドにブロードキャストできる

16スレッドが同じアドレスにアクセスすると も効率がよい

2. コンスタントメモリキャッシュ

SMごとに存在する独自のオンチップキャッシュ

他のHalf Warpがキャッシュされたデータへアクセスしても,コンスタントメモリからの読込が発生しない

2016/01/13GPGPU講習会212

Page 213: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Tesla世代)

オフチップメモリ(DRAM)からの読込量を抑制

オフチップメモリ(DRAM)からの読込による実行速度低下を回避

コンスタントメモリへのアクセスの制約

Half Warp内のスレッド全てが異なるコンスタントメモリのアドレスを参照すると,読込が逐次化

読込命令の処理に 悪で16倍の時間を要する

2016/01/13GPGPU講習会213

Page 214: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Tesla世代)

Half Warp内のスレッド全てが同じメモリアドレスにアクセス

1スレッドの読込をブロードキャストによって残りのスレッドが共有

他のHalf Warpも同じメモリアドレスにアクセス

データがキャッシュされているため,キャッシュから高速に読み出し

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0 0

2016/01/13GPGPU講習会214

Half Warp内でのスレッドID

メモリアドレス

Page 215: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Tesla世代)

Half Warp内のスレッド全てが異なるメモリアドレスにアクセス

読込が逐次化

読込処理の時間は,Half Warpがアクセ

スするコンスタントメモリアドレスの数に比例

悪で処理に16倍の時間がかかる

おそらくグローバルメモリアクセスよりも遅くなる

2016/01/13GPGPU講習会

15

14

13

12

11

10

9

8

7

6

5

4

3

2

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1 1

0 0

215

Page 216: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Fermi世代)

コンスタントメモリへ高速にアクセスできる要因

1. ブロードキャストによるデータの分配

32スレッド(Warp)単位でアクセスし,1回の読込を他のスレッドにブロードキャストできる

32スレッドが同じアドレスにアクセスすると も効率がよい

2. コンスタントメモリキャッシュ

SMごとに存在する独自のオンチップキャッシュ

他のWarpがキャッシュされたデータへアクセスしても,コンスタントメモリからの読込が発生しない

2016/01/13GPGPU講習会216

Page 217: GPGPU Seminar (GPGPU and CUDA Fortran)

コンスタントメモリへのアクセス(Fermi世代)

オフチップメモリ(DRAM)からの読込量を抑制

オフチップメモリ(DRAM)からの読込による実行速度低下を回避

コンスタントメモリへのアクセスの制約

Warp内のスレッド全てが異なるコンスタントメモリのアドレスを参照すると,読込が逐次化

読込命令の処理に 悪で32倍の時間を要する

2016/01/13GPGPU講習会217

Page 218: GPGPU Seminar (GPGPU and CUDA Fortran)

0 16 32 48 64 80 96 112

コンスタントメモリへのアクセス(Fermi世代)

Warp内のスレッド全てが同じコンスタントメモリアドレスにアクセス

1スレッドの読込をブロードキャストによって残りのスレッドが共有

他のWarpも同じメモリアドレスにアクセス

データがキャッシュされているため,キャッシュから高速に読み出し

2016/01/13GPGPU講習会218

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Page 219: GPGPU Seminar (GPGPU and CUDA Fortran)

0 16 32 48 64 80 96 112

コンスタントメモリへのアクセス(Fermi世代)

Warp内のスレッド全てが異なるコンスタントメモリアドレスにアクセス

読込が逐次化

読込処理の時間は,Warpがアクセスするコンスタントメモリアドレスの数に比例

悪で処理に32倍の時間がかかる

おそらくグローバルメモリアクセスよりも遅くなる

2016/01/13GPGPU講習会219

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31