GPUコンピューティング入門 2015.08.26 エヌビディア合同会社 CUDAエンジニア 村上真奈 Agenda エヌビディアのGPUについて(20分) GPUコンピューティングとは?(10分) OpenACC入門(90分) CUDA入門(90分) 2 Agenda エヌビディアのGPUについて(20分) GPUコンピューティングとは?(10分) OpenACC入門(90分) CUDA入門(90分) 3 18 1993年創立 共同創業者、社長兼CEO : ジェンスン・フアン 1999年、銘柄コード「NVDA」で NASDAQ に株式上場 1999年に GPU を発明し、現在までに10億点以上を出荷 2015年会計年度の収益: 46.8億ドル 世界中に 9,300 名の従業員 7,300 件の特許取得済み資産 本社: カリフォルニア州サンタクララ 4 3 ゲーミング オートモーティブ エンタープライズ HPC &クラウド 当社は、ビジュアル・コンピューティングが必要不可欠で重要な価値を持つ大規模な市場に特化し、プロセッサのプラット フォーム、ソフトウェア、システム、サービスを提供しています。当社はPCテクノロジ、データセンター・テクノロジ、モバイル・テ クノロジの革新に取り組んでいます。そして、当社の発明は さまざまな業界のOEM製品の原動力となっています。 5 6 東京工業大学 TSUBAME 2.5 4,224枚の Tesla K20X 単精度理論性能値で日本 No.1 スパコン 17PFLOPS SP 7 NVIDIA GPU OFFERS TOP LEVEL COMPUTATIONAL PERFORMANCE WITH HIGH ENERGY EFFICIENVY From SC TOP500 Nov., 2014 Rank Country Site System 1 China National Super Computer Center in Guangzhou Tianhe-2 (MilkyWay-2) - TH-IVB-FEP Cluster, Intel Xeon E5-2692 12C 2.200GHz, TH Express-2, Intel Xeon Phi 31S1P 2 US DOE/SC/Oak Ridge National laboratory Titan-Cray XK7, Opt. 6274 16C 2.2GHz, NVIDIA K20x 3 US DOE/NNSA/LLNL Sequoia - BlueGene/Q, Power BQC 16C 1.60 GHz, Custom 4 Japan RIKEN Advanced Institute for Computational Science (AICS) K computer, SPARC64 VIIIfx 2.0GHz, Tofu interconnect 5 US DOE/SC/Argonne National Mira - BlueGene/Q, Power BQC 16C 1.60GHz, Laboratory Custom Cores Rmax (TFlop/s) Rpeak (TFlop/s) Power (kW) 3,120,000 33,862.70 54,902.40 17,808 560,640 17,590.00 27,112.50 8,209 1,572,864 17,173.20 20,132.70 7,890 705,024 10,510.00 11,280.40 12,660 786,432 8,586.60 10,066.30 3,945 In GREEN500 the most energy efficient super computers, NVIDIA GPU drives 8 systems out of TOP 10. 8 REAL WORLD EXAMPLE Rendering 30-second Animation at Renault 9 Deep Learning における GPU の活用 Deep Learning に GPU を活用 Input Result 110 28% 26% GPU 対応した Deep Learning 用ツール 16% 60 person dog 12% 0 0 4 2010 2011 2012 7% 2013 2014 chair Caffe Torch Theano Cuda-convnet cuDNN cuBLAS 10 GPUロードマップ 72 Volta 60 SGEMM / W 48 Pascal 36 24 Maxwell 12 Fermi Tesla 0 2008 Kepler 2010 2012 2014 2016 2018 11 TESLA KEPLER FAMILY WORLD’S FASTEST AND MOST EFFICIENT HPC ACCELERATORS CFD, BioChemistry, Neural Networks, High Energy Physiscs, Graph analytics, Material Science, BioInformatics, M&E Weather & Climate, Physics, BioChemistry, CAE, Material Science Image, Signal, Video, Seismic Memory Bandwidth (ECC off) GPUs Single Precision Peak (SGEMM) Double Precision Peak (DGEMM) Memory Size PCIe Gen System Solution K80 8.74 TF (5.6TF) 2.91TF (1.87TF) 24 GB 480GB/s (240GB/s x2) Gen 3 Server + Workstation K40 4.29 TF (3.22TF) 1.43 TF (1.33 TF) 12 GB 288 GB/s Gen 3 Server + Workstation K20X 3.95 TF (2.90 TF) 1.32 TF (1.22 TF) 6 GB 250 GB/s Gen 2 Server only K20 3.52 TF (2.61 TF) 1.17 TF (1.10 TF) 5 GB 208 GB/s Gen 2 Server + Workstation K10 4.58 TF 0.19 TF 8 GB 320 GB/s Gen 3 Server only 12 THE NEW QUADRO FAMILY M6000 K6000 K5200 K4200 K2200 K620 K420 2880 2304 1344 640 384 192 Single Precision 5.2 TFLOPs 3.1 TFLOPs 2.1 TFLOPs 1.3 TFLOPs 0.8 TFLOPs 0.3 TFLOPs PCIe Gen 3.0 # CUDA Cores 3072 2.0 Memory Size 12GB 12 GB 8 GB 4 GB 4 GB 2 GB 1 GB Memory BW 317 GB/s 288 GB/s 192 GB/s 173 GB/s 80 GB/s 29 GB/s 29 GB/s 2x DP* + 2x DVI 2x DP* + DVI Slots + Display Connectors 2x DP* + 2x DVI 2x DP* + 2x DVI 4096 x 2160 Max Resolution Max Displays 4 250W 225 W * DP + DVI DP* + DVI 3840 x 2160 4 4 4 SDI, SYNC, STEREO, MOSAIC, NVIEW Pro Features Board Power 4 2x DP* + DVI 150 W * DisplayPort 1.2 multi-streaming can be used to drive multiple displays from a single DP connector 108 W 4 4 MOSAIC, NVIEW 68 W 45 W 41 W 13 前世代比 3倍の性能 Double Precision FLOPS (DGEMM) Tesla M2090 Tesla K40 CUDA コア数 512 2880 倍精度演算性能 DGEMM 665 G 400 GF 1.43 TF 1.33 TF 単精度演算性能 SGEMM 1.33 TF 0.89 TF 4.29 TF 3.22 TF メモリバンド幅 178 GB/s 288 GB/s メモリサイズ 6 GB 12 GB 消費電力 225W 235W 1.33 TFLOPS 1.4 TFLOPS 1.2 1 0.8 0.6 0.4 0.40 TFLOPS 0.2 0 Tesla M2090 Tesla K40 Single Precision FLOPS (SGEMM) 3.5 3.22 TFLOPS 3 TFLOPS 2.5 2 1.5 1 0.89 TFLOPS 0.5 0 Tesla M2090 Tesla K40 14 NVIDIA GPU SCALABLE ARCHITECTURE FROM SUPER COMPUTER TO MOBILE Tesla Tegra In Super Computers Quadro In Work Stations GeForce In PCs Mobile GPU In Tegra 17 2015 TEGRA X1 MOBILE SUPERCHIP 256-core Maxwell GPU | 8-core 64-bit CPU | 4Kp60 10-bit H.265/VP9 19 TEGRA X1 OVERVIEW CPU: Quad ARM Cortex A57/A53 64/32b CPU that delivers Performance and Power Efficiency GPU: Next Generation 256Core Maxwell GPU that deliver Class-Leading Performance and Power Efficiency End-to-End 4k 60fps Pipeline that delivers Premium 4K Experience Built on 20nm Process Technology 20 BRIDGING THE GAP Maxwell Kepler Advancements Fermi Tesla Tegra X1 Tegra K1 GEFORCE ARCHITECTURE Tegra 4 Tegra 3 MOBILE ARCHITECTURE 21 WORLD’S 1ST TERAFLOPS MOBILE PROCESSOR Tegra X1 1200 Tegra X1 (FP16) Core i7 1000 GPU GPU CPU 800 CPU GFLOPS FP16/INT16 600 400 Tegra K1 200 Tegra 4 Tegra 2 Tegra 3 0 TIME Note: 4790K Core i7, CPU @ 4GHz, GPU 22 @ 350 MHz Agenda エヌビディアのGPUについて(20分) GPUコンピューティングとは?(10分) OpenACC入門(90分) CUDA入門(90分) 24 NVIDIA GPU の歴史 2010 Fermi 3 Billion Transistors GPU 2012 Kepler 7 Billion Transistors 統合シェーダ + CUDA 25 GPUの構造 GPU PCI Express Giga Thread Engine SM SM SM SM … L2 Cache DRAM 26 GPUアーキテクチャ概要 PCI I/F SM SM SM SM SM SM SM SM ホスト接続インタフェース Giga Thread Engine SMに処理を割り振るスケジューラ DRAM (384-bit, GDDR5) SM SM Kepler GK110 SM SM SM SM SM 全SM、PCI I/Fからアクセス可能なメモリ (デバイスメモリ, フレームバッファ) L2 cache (1.5MB) 全SMからアクセス可能なR/Wキャッシュ SM (Streaming Multiprocessor) 「並列」プロセッサ 27 SM (STREAMING MULTIPROCESSOR) CUDA core GPUスレッドはこの上で動作 Kepler: 192個 Other units DP, LD/ST, SFU Register File (65,536 x 32bit) Shared Memory/L1 Cache (64KB) Read-Only Cache(48KB) Kepler GK110 28 COMPUTE CAPABILITY • GPUコアアーキテクチャのバージョン • CUDA GPUs : https://developer.nvidia.com/cuda-gpus • アーキテクチャは進化する • 高効率の命令実行 • 省消費電力 29 SM ARCHITECTURE VS COMPUTE CAPABILITY Instruction Cache Scheduler Scheduler Dispatch Dispatch Register File 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 Load/Store Units x 16 Special Func Units x 4 Interconnect Network 64K Configurable Cache/Shared Mem Uniform Cache Fermi CC 2.0 : 32 cores / SM Kepler CC 3.5 : 192 cores / SMX Maxwell CC 5.0 : 128 cores / SMM 30 GPUコンピューティングとは? • GPUは何の略? • Graphics Processing Unit • 3DCG等、画像データ処理の為のデバイス • GPUによる汎用コンピューティングのこと • 計算科学など様々な用途でGPUを利用する 31 ヘテロジニアス・コンピューティング CPU 逐次処理に最適化 GPU Accelerator 並列処理に最適化 32 GPUアプリケーションの例 画像処理 コンピュータビジョン 医療画像 防衛 計算化学 気象 金融工学 バイオ 数値解析 33 GPUアクセラレーションの実現方法 アプリケーション GPU ライブラリ ライブラリを呼び出すだけ 簡単に高速化を実現 OpenACC ディレクティブ CUDA 既存コードにディレクティブ を挿入して高速化 重要なコードをCUDAで記述 最も自由度が高い 34 GPUアクセラレーションの実現方法 簡単 ライブラリ ライブラリを呼び出すだけで、高速化が可能 ライブラリとして提供されている機能のみ高速化が可能 OpenACC 既存のC言語やFortranのコードにディレクティブを挿入するだけで簡単に高速化 最適化はコンパイラが行う為、細かいチューニングを行う事は出来ない CUDA 高速化 自由度が最も高く、細かいチューニングが可能 CUDAでのプログラミングを学ぶ必要がある 35 Agenda エヌビディアのGPUについて(20分) GPUコンピューティングとは?(10分) OpenACC入門(90分) CUDA入門(90分) 36 GPUアクセラレーションの実現方法 アプリケーション GPU ライブラリ ライブラリを呼び出すだけ 簡単に高速化を実現 OpenACC ディレクティブ CUDA 既存コードにディレクティブ を挿入して高速化 重要なコードをCUDAで記述 最も自由度が高い 37 OPENACC 標準的なGPUディレクティブ シンプル: ディレクティブを挿入するのみ。コードを変更する事なく高速化 オープン: OpenACCはマルチコアプロセッサで並列化を行う為のオープン標準 柔軟: GPU ディレクティブは、高い並列性を保ちつつ同一コードで複数のアーキテクチャに対応可能 38 OpenACCメンバーとパートナー 39 コンパイラとツール 2013年12月~ コンパイラ 2014年1月~ 2015年(予定) OpenACC 2.0対応 デバッグツール 40 簡単に高速 自動車 金融 生命科学 Real-Time Object Detection Valuation of Stock Portfolios using Monte Carlo Interaction of Solvents and Biomolecules Global Manufacturer of Navigation Systems Global Technology Consulting Company University of Texas at San Antonio 40時間で5倍 4時間で2倍 8時間で5倍 41 簡単に始められる 大学関係者の方は無償で使用可能に 下記のサイトからOpenACC toolkitをダウンロード https://developer.nvidia.com/openacc PGIコンパイラ/MPI/CUDAなど 一式が簡単にインストール可能 42 実行モデル アプリケーション・コード GPU $acc parallel 計算の 重い部分 CPU $acc end parallel 並列部分は GPUコードを生成 逐次部分は CPUコードを生成 43 OpenACC ディレクティブ CPU GPU コンパイラへシンプルなヒント Program myscience ... serial code ... !$acc kernels do k = 1,n1 do i = 1,n2 ... parallel code ... enddo enddo !$acc end kernels ... End Program myscience コンパイラがコードを並列化 コンパイラへの OpenACC ヒント 並列部はGPUで 逐次処理はCPUで動作 Fortran または C言語 のオリジナルコード 44 OpenMPとOpenACCの比較 OpenMP CPU main() { double pi = 0.0; long i; #pragma omp parallel for reduction(+:pi) for (i=0; i<N; i++) { double t = (double)((i+0.05)/N); pi += 4.0/(1.0+t*t); } CPUコアに printf(“pi = %f\n”, pi/N); 計算処理を分散 } OpenACC CPU GPU main() { double pi = 0.0; long i; #pragma acc kernels for (i=0; i<N; i++) { double t = (double)((i+0.05)/N); pi += 4.0/(1.0+t*t); } printf(“pi = %f\n”, pi/N); } GPUコアに 計算処理を分散 45 OpenACC ディレクティブ 構文 C/C++ #pragma acc 指示行 [節[,]節] …] { structured block } Fortran !$acc 指示行 [節[,]節] …] { structured block } !$acc end directive 46 OpenACC構文: parallel 指示行 • parallel : 並列に実行される領域を指示行で指定 #pragma acc parallel for(int i=0;i<n;i++){ a[i] = 0.0; b[i] = 1.0; c[i] = 2.0; } kernel 1 Kernel(カーネル): GPU上で実行される 関数 47 OpenACC構文: kernels 指示行 • kernels : 複数のカーネルを作成 #pragma acc kernels for(int i=0;i<n;i++){ a[i] = 0.0; b[i] = 1.0; c[i] = 2.0; } #pragma acc kernels for(int i=0;i<n;i++){ a[i] = b[i] + c[i]; } kernel 1 Kernel(カーネル): GPU上で実行される 関数 kernel 2 48 [C tips]: restrict修飾子 コンパイラに対して明示的にrestrict修飾子を指定。ポインタのエイリアシング を制限 例) float *restrict ptr OpenACCコンパイラにrestrict修飾子をつけ変数の独立性を伝える 独立性の保障がないとコンパイラは並列化を行う事が出来ない 49 http://en.wikipedia.org/wiki/Restrict 例:SAXPY (Y=A*X+Y) Trivial first example Apply a loop directive Learn compiler commands int main(int argc, char **argv) { int N = 1<<20; // 1 million floats if (argc > 1) N = atoi(argv[1]); float *x = (float*)malloc(N * sizeof(float)); float *y = (float*)malloc(N * sizeof(float)); *restrict: #include <stdlib.h> “yはxのエイリアスでない”と明示的 に指定 void saxpy(int n, float a, float *x, float *restrict y) { #pragma acc kernels for (int i = 0; i < n; ++i) y[i] = a * x[i] + y[i]; } for (int i = 0; i < N; ++i) { x[i] = 2.0f; y[i] = 1.0f; } saxpy(N, 3.0f, x, y); return 0; } 50 C言語:SAXPY (Y=A*X+Y) OpenMP OpenACC void saxpy(int n, float a, float *x, float *restrict y) { #pragma omp parallel for for (int i = 0; i < n; ++i) y[i] += a*x[i]; } void saxpy(int n, float a, float *x, float *restrict y) { #pragma acc parallel copy(y[:n]) copyin(x[:n]) for (int i = 0; i < n; ++i) y[i] += a*x[i]; } ... saxpy(N, 3.0, x, y); ... ... saxpy(N, 3.0, x, y); ... omp acc データの移動 51 Fortran: SAXPY (Y=A*X+Y) OpenMP OpenACC subroutine saxpy(n, a, X, Y) real :: a, X(:), Y(:) integer :: n, i subroutine saxpy(n, a, X, Y) real :: a, Y(:), Y(:) integer :: n, i !$omp parallel do do i=1,n Y(i) = a*X(i)+Y(i) enddo !$omp end parallel do end subroutine saxpy !$acc parallel copy(Y(:)) copyin(X(:)) do i=1,n Y(i) = a*X(i)+Y(i) enddo !$acc end parallel end subroutine saxpy ... call saxpy(N, 3.0, x, y) ... ... call saxpy(N, 3.0, x, y) ... 52 コンパイルオプション C: pgcc –acc -ta=nvidia -Minfo=accel –o saxpy_acc saxpy.c Fortran: pgf90 –acc -ta=nvidia -Minfo=accel –o saxpy_acc saxpy.f90 ターゲットに nvidiaを指定 コンパイラがGPU用のコードを生 成する際の情報を表示する 53 簡単にコンパイル OpenMP / OpenACC void saxpy(int n, float a, float *x, float *restrict y) $ pgcc –acc {–ta=nvidia –Minfo=accel saxpy.c #pragma acc parallel copy(y[:n]) copyin(x[:n]) saxpy: #pragma omp parallel for 16, Generating present_or_copy(y[:n]) for present_or_copyin(x[:n]) (int i = 0; i < n; ++i) Generating += code a*x[i]; Generatingy[i] Tesla } parallelizable 19, Loop is Accelerator kernel generated ... 19, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */ saxpy(N, 3.0, x, y); ... 54 簡単に実行 OpenMP / OpenACC void saxpy(int n, float a, float *x, float *restrict y) { $ pgcc -Minfo -acc saxpy.c $ nvprof ./a.out #pragma acc kernels copy(y[:n]) copyin(x[:n]) saxpy: ==10302== NVPROF ispresent_or_copy(y[:n]) profiling process #pragma omp parallel for 10302, command: ./a.out 16, Generating ==10302== Profiling application: for present_or_copyin(x[:n]) (int i = 0; i ./a.out < n; ++i) Generating ==10302== Profiling result: y[i] += code a*x[i]; Generating Tesla Time(%) Calls Avg Min Max Name } parallelizable 19, Loop Time is 62.95% Accelerator 3.0358ms 1.5179ms 1.5172ms 1.5186ms [CUDA memcpy HtoD] kernel2generated 31.48% 19, 1.5181ms 1 1.5181ms 1.5181ms/*1.5181ms [CUDA memcpy DtoH] ... #pragma acc loop gang, vector(128) blockIdx.x threadIdx.x */ 5.56% 268.31us 268.31us 268.31us saxpy_19_gpu saxpy(N, 3.0,1 x,268.31us y); ... 55 例: ヤコビ反復法 正しい値になるように反復計算を行う。隣接点の平均値で値を更新 連立一次方程式を解く為のオーソドックスな手法 例: 2次元ラプラス方程式: 𝛁𝟐 𝒇(𝒙, 𝒚) = 𝟎 A(i,j+1) A(i-1,j) A(i,j) A(i+1,j) 𝐴𝑘+1 𝑖, 𝑗 = 𝐴𝑘 (𝑖 − 1, 𝑗) + 𝐴𝑘 𝑖 + 1, 𝑗 + 𝐴𝑘 𝑖, 𝑗 − 1 + 𝐴𝑘 𝑖, 𝑗 + 1 4 A(i,j-1) 56 ヤコビ反復法(アルゴリズム) A(i,j+1) while ( error > tol ) { error = 0.0; for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i])); } } A(i-1,j) A(i,j) A(i+1,j) A(i,j-1) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } 57 並列領域 (OpenMP) while ( error > tol ) { error = 0.0; #pragma omp parallel for shared(m, n, Anew, A) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma omp parallel for shared(m, n, Anew, A) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } 58 並列領域 (OpenACC) while ( error > tol ) { error = 0.0; #pragma acc kernels for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } Parallels と Kernels — 並列領域を指示 Parallels — 並列実行スタート Kernels — 複数のカーネル 59 [PGI tips] コンパイラメッセージ $ pgcc –acc –ta=nvidia –Minfo=accel jacobi.c jacobi: 44, Generating copyout(Anew[1:4094][1:4094]) Generating copyin(A[:][:]) Generating Tesla code 45, Loop is parallelizable 46, Loop is parallelizable Accelerator kernel generated 45, #pragma acc loop gang /* blockIdx.y */ 46, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */ 49, Max reduction generated for error 60 並列領域 (KERNELS CONSTRUCT) while ( error > tol ) { error = 0.0; Parallels と Kernels — 並列領域を指示 #pragma acc kernels for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; — 並列走行の開始 $ pgcc –acc –ta=nvidia error = max(error, -Minfo=accel abs(Anew[j][i] -jacobi.c A[j][i]); jacobi: } } 59, Generating present_or_copyout(Anew[1:4094][1:4094]) Parallels Kernels — 複数のGPUカーネル Generating present_or_copyin(A[:][:]) #pragma acc kernels Generating code{ for (int j = 1; j <Tesla N-1; j++) for (int = 1; i < M-1; i++) { 61, Loop iis parallelizable A[j][i] = Anew[j][i]; 63, Loop is parallelizable } Accelerator kernel generated } 61, #pragma acc loop gang /* blockIdx.y */ } 63, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */ Max reduction generated for error 61 データ転送 (DATA CLAUSE) while ( error > tol ) { error = 0.0; #pragma acc kernels for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + $ pgcc –acc –ta=nvidia -Minfo=acc jacobi.c A[j-1][i] + A[j+1][i]) * 0.25; jacobi: error = max(error, abs(Anew[j][i] - A[j][i]); } 59, Generating present_or_copyout(Anew[1:4094][1:4094]) } Generating present_or_copyin(A[:][:]) Generating #pragma acc kernelsTesla code for (int j =is 1; parallelizable j < N-1; j++) { 61, Loop for (int i = 1; i < M-1; i++) { 63, Loop is parallelizable A[j][i] = Anew[j][i]; Accelerator kernel generated } } 61, #pragma acc loop gang /* blockIdx.y */ } 63, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */ Max reduction generated for error 62 コードの解析 実行状況を確認。ボトルネックはどの部分か? 実行時間の内訳を調べる 63 -ta=nvidia,time コンパイルオプションに-ta=nvidia,timeを追加して、コンパイル・実行 /home/OpenACC/C/jacobi.c Kernel実行:196ms jacobi NVIDIA devicenum=0 time(us): 4,595,922 44: compute region reached 200 times 46: kernel launched 200 times grid: [32x4094] block: [128] データコピー(H->D):1087ms device time(us): total=196,036 max=1,053 min=931 avg=980 elapsed time(us): total=201,618 max=1,084 min=958 avg=1,008 46: reduction kernel launched 200 times grid: [1] block: [256] device time(us): total=39,356 max=206 min=187 avg=196 elapsed time(us): total=42,155 max=227 min=200 avg=210 44: data region reached 200 times 44: data copyin transfers: 800 device time(us): total=1,087,027 max=1,374 min=1,354 avg=1,358 データコピーがボトルネック 53: compute region reached 200 times 55: kernel launched 200 times 64 grid: [32x4094] block: [128] NVIDIA Visual Profiler (NVVP)を使用 65 NVVPによる解析: データ転送がボトルネック 利用率:低い 1 cycle GPU kernel GPU kernel 66 計算処理とデータ転送 CPU Memory データ転送 GPU Memory PCI 計算オフロード 計算オフロード、データ転送、両方を考慮する必要がある 67 OpenACC構文: データ 指示行 • copy ( X ) • copyin(list) + copyout(list) • copyin ( X ) • アクセラレータ領域に入る際にGPU上に X 用のメモリを確保し、ホストからGPU(デバ イス)へ X を転送する • copyout ( X ) • アクセラレータ領域に入る際にGPU上に X 用のメモリを確保し、アクセラレータ領域か ら出る時にGPU(デバイス)からホストへ X を転送する • create ( X ) • アクセラレータ領域に入る時にGPU上に X 用のメモリが確保される (転送はされない) • present ( X ) • アクセラレータ領域に入る時に X が既にデバイス上に存在することを示す 68 OpenACC構文: データ 指示行 • pcopy ( X ) • present (X) + copy(X) • pcopyin ( X ) • present (X) + copyin(X) • pcopyout ( X ) • present (X) + copyout(X) • pcreate ( X ) • present (X) + create(X) 69 データ転送 (DATA CLAUSE) while ( error > tol ) { error = 0.0; #pragma acc kernels \ pcopyout(Anew[1:N-2][1:M-2]) pcopyin(A[0:N][0:M]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels \ pcopyout(A[1:N-2][1:M-2]) pcopyin(Anew[1:N-2][1:M-2]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } copyin (HostGPU) copyout (HostGPU) copy create present pcopyin pcopyout pcopy pcreate 70 データ転送 (DATA CLAUSE) while ( error > tol ) { error = 0.0; #pragma acc kernels \ pcopy(Anew[:][:]) pcopyin(A[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels \ pcopy(A[:][:]) pcopyin(Anew[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } copyin (HostGPU) copyout (HostGPU) copy create present pcopyin pcopyout pcopy pcreate 71 過剰なデータ転送 while ( error > tol ) { error = 0.0; #pragma acc kernels \ pcopy(Anew[:][:]) pcopyin(A[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels \ pcopy(A[:][:]) pcopyin(Anew[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } 72 過剰なデータ転送 GPU Host while ( error > tol ) { error = 0.0; #pragma acc kernels \ pcopy(Anew[:][:]) \ pcopyin(A[:][:]) { } #pragma acc kernels \ pcopy(A[:][:]) \ pcopyin(Anew[:][:]) { } } copyin copyout copyin copyout #pragma acc loop reduction(max:error) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } 73 データ領域 (data construct) #pragma acc data pcopy(A, Anew) while ( error > tol ) { error = 0.0; #pragma acc kernels pcopy(Anew[:][:]) pcopyin(A[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels pcopy(A[:][:]) pcopyin(Anew[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } copyin (CPUGPU) copyout (CPUGPU) copy create present pcopyin pcopyout pcopy pcreate } 74 データ領域 (data CONSTRUCT) #pragma acc data pcopy(A) create(Anew) while ( error > tol ) { error = 0.0; #pragma acc kernels pcopy(Anew[:][:]) pcopyin(A[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } #pragma acc kernels pcopy(A[:][:]) pcopyin(Anew[:][:]) for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } copyin (CPUGPU) copyout (CPUGPU) copy create present pcopyin pcopyout pcopy pcreate 75 適正なデータ転送 GPU Host #pragma acc data \ pcopy(A) create(Anew) while ( error > tol ) { error = 0.0; copyin #pragma acc kernels \ pcopy(Anew[:][:]) \ pcopyin(A[:][:]) { for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { Anew[j][i] = (A[j][i+1] + A[j][i-1] + A[j-1][i] + A[j+1][i]) * 0.25; error = max(error, abs(Anew[j][i] - A[j][i]); } } } #pragma acc kernels \ pcopy(A[:][:]) \ pcopyin(Anew[:][:]) { for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } } copyout 76 データ転送の削減 (NVVP) 稼働率:高い 1 cycle 77 GPUアクセラレーションの実現方法 アプリケーション GPU ライブラリ ライブラリを呼び出すだけ 簡単に高速化を実現 OpenACC ディレクティブ CUDA 既存コードにディレクティブ を挿入して高速化 重要なコードをCUDAで記述 最も自由度が高い 78 CUDAとは? • Compute Unified Device Architectureの略 • NVIDIA GPU上の汎用並列計算プラットフォーム • Linux・Windows・MacOS X(+Android)で動作 • 現在、7.0が最新 • ※7.5RCも公開中 79 CUDA 開発・実行環境 ライブラリ・ミドルウェア cuDNN cuSOLVER cuRAND cuFFT cuBLAS cuSPARSE Thrust NPP NVRTC プログラミング言語 C C++ C++11 Fortran Java Python etc.. NVIDIA-GPUs(ハードウェア) HyperQ Dynamic Parallelism 開発環境 MATLAB Mathematica etc.. NVCC (CUDA compiler) CUDA-GDB Profiler Nsight IDE GPU Direct 82 CUDA 開発・実行環境 ライブラリ・ミドルウェア cuDNN cuSOLVER cuRAND cuFFT cuBLAS cuSPARSE Thrust NPP NVRTC プログラミング言語 C C++ C++11 Fortran Java Python etc.. NVIDIA-GPUs(ハードウェア) HyperQ Dynamic Parallelism 開発環境 MATLAB Mathematica etc.. NVCC (CUDA compiler) CUDA-GDB Profiler Nsight IDE GPU Direct 83 進化するハードウェア NVIDIA-GPUS 84 進化するハードウェア NVIDIA-GPUS Hyper-Q Dynamic Parallelism GPU Direct 85 CUDA 開発・実行環境 ライブラリ・ミドルウェア cuDNN cuSOLVER cuRAND cuFFT cuBLAS cuSPARSE Thrust NPP NVRTC プログラミング言語 C C++ C++11 Fortran Java Python etc.. NVIDIA-GPUs(ハードウェア) HyperQ Dynamic Parallelism 開発環境 MATLAB Mathematica etc.. NVCC (CUDA compiler) CUDA-GDB Profiler Nsight IDE GPU Direct 87 プログラミング言語 C C++ CUDA C CUDA C++(C++11),Thrust Python PyCUDA Fortran CUDA Fortran その他 F#, MATLAB, Mathematica, … 88 CUDA 開発・実行環境 ライブラリ・ミドルウェア cuDNN cuSOLVER cuRAND cuFFT cuBLAS cuSPARSE Thrust NPP NVRTC プログラミング言語 C C++ C++11 Fortran Java Python etc.. NVIDIA-GPUs(ハードウェア) HyperQ Dynamic Parallelism 開発環境 MATLAB Mathematica etc.. NVCC (CUDA compiler) CUDA-GDB Profiler Nsight IDE GPU Direct 90 CUDAライブラリ cuDNN ディープニューラルネットワーク 計算用ライブラリ cuFFT 高速フーリエ変換ライブラリ cuSOLVER cuRAND cuSPARSE 乱数生成ライブラリ 疎行列計算用ライブラリ cuBLAS NPP Thrust 線形代数演算LAPACK用 ライブラリ 線形代数計算用ライブラリ 動画像処理・信号処理用 ライブラリ C++テンプレートライブラリ 91 CUDAを使用したソフトウェア MATLAB Mathematica OpenCV ArrayFire etc… Caffe torch theano 92 CUDA 開発・実行環境 ライブラリ・ミドルウェア cuDNN cuSOLVER cuRAND cuFFT cuBLAS cuSPARSE Thrust NPP NVRTC プログラミング言語 C C++ C++11 Fortran Java Python etc.. NVIDIA-GPUs(ハードウェア) HyperQ Dynamic Parallelism 開発環境 MATLAB Mathematica etc.. NVCC (CUDA compiler) CUDA-GDB Profiler Nsight IDE GPU Direct 94 開発環境 DEBUG & ANALYSIS NVCC CUDA用コンパイラ CUDA-GDB CUDA用デバッガ(Linux,Mac) CUDA-MEMCHECK GPUメモリエラーチェックツール Nsight IDE Profiler CUDA統合開発環境(Linux,Windows) CUDA解析ツール 95 NSIGHT VISUAL STUDIO EDITION 96 ここまでの復習 • CUDAでは、様々なプログラミング言語やライブラリを使う事が可能 • ケースによって最適なものを選択すれば良い • 既存のライブラリやミドルウェアを有効活用する • CUDAはロードマップが存在し、進化し続けている • よりプログラミングしやすく • パフォーマンスが出やすいように 97 CUDA C/C++アプリケーション入門 今回は、CUDA C/C++で説明します 98 典型的な装置構成 CPUにつながった外部演算装置 GPU PC CPU (数コア) Giga Thread Engine 制御 PCIe ホスト側DRAM SM SM SM SM … L2 Cache DRAM 転送 99 典型的な実行例 プログラム 開始 GPUはCPUからの制御 で動作する。 CPU データ 転送 CUDA カーネル 実行 完了 待ち データ 転送 入力データは CPU→GPUへと転送。 結果は、 GPU→CPUと転送 GPU GPUでの演算 GPU上に常駐する プログラムはない。 100 CUDA C/C++用語 • GPUで実行される関数をカーネル(kernel)と呼ぶ • CPUで実行されるコードをホストコード、GPUで実行されるコードを デバイスコードと呼ぶ • データ並列を表現する為に以下の概念を用いる • グリッド (grid) • ブロック (block) • スレッド (thread) 101 グリッド・ブロック・スレッド • グリッド (grid) • ブロックをまとめた物 • ブロック (block) スレッドをまとめた物 • 1ブロックあたり最大1024スレッド • スレッド (thread) • カーネルを動作させる最小単位 Thread Thread Block1 Thread Thread Thread Thread Block2 Grid Thread Thread … • Thread Thread Block0 Thread Thread Thread Thread Block n Thread Thread 103 グリッド・ブロック・スレッド GPU CUDA Block0 Grid GPU Thread Thread Thread Thread Block1 Thread Thread Thread Thread Block2 Thread Thread Thread Thread … … Block n Thread Thread Thread Thread SM SM SM SM core 105 カーネル実行の流れ • Giga Thread EngineがブロックをSMに割り当てる Grid Block0 Giga Thread Engine Block1 Block2 Block3 Block4 … BlockN Block4 107 カーネル実行の流れ • SMの中のスケジューラがコアにスレッドを投入する Grid Block 0 ワープを投入 Thread Thread Thread Thread 32スレッド単位で 投入 Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread … BlockN 108 BLOCK は SM上で実行 • Block ⇒ 1 SM • 複数のSMにまたがらない。 (SM中では、複数Blockが実行される場合もある。) • Block内部では、SMXのリソースを活用可能 • 各々のBlockは、独立に、非同期に処理を実行する。 • 実行順序の保証はない。 • Block間の通信・同期は行わない。 109 例: 一次元配列の加算 • 配列Aと配列Bの加算結果を配列Cに書き込む [0] [1] A 10 1 [2] [3] [4] [5] [6] [7] 8 7 14 13 2 5 [8] [9] [10] [11] [12] [13] [14] [15] 6 15 3 9 12 11 0 4 + + + + + + + + + + + + + + + + B 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 … … = = = = = = = = = = = = = = = = C 10 2 10 10 18 18 8 12 14 24 13 20 24 24 14 19 … 110 例: 一次元配列の加算(CPU) • 配列の0番から逐次加算していく CPU C[0] = A[0] + B[0]; C[1] = A[1] + B[1]; C[2] = A[2] + B[2]; for(int i=0 C[3]; =i<nMatrixSize A[3] + B[3]; ; i++) { C[4]= =A[i] A[4] + B[4]; C[i] + B[i]; C[5] = A[5] + B[5]; } … 111 例: 一次元配列の加算(GPU) [0] [1] A B 10 1 [2] [3] [4] [5] [6] [7] 8 7 14 13 2 5 [8] [9] [10] [11] [12] [13] [14] [15] 6 15 3 9 12 11 0 4 + + + + + + + + + + + + + + + + … 0 1 2 5 3 4 6 7 8 9 10 11 12 13 14 15 = = = = = = = = = = = = = = = = C … 13 20 24 24 10 2 10 10 18 18 8 12 14 24 T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 Block0 Block1 Block2 14 19 ……… … … BlockN 112 ブロックIDとスレッドID ブロックIDとスレッドIDから、インデックス(グローバルID)を生成する インデックスを用いて各スレッドから、グローバルメモリへアクセスする index = blockDim.x * blockIdx.x + threadIdx.x; × + 2 1 6 8 Thread Block 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 0 1 2 3 0 11 4 5 12 13 14 15 16 17 0 1 2 3 4 5 2 113 例: 一次元配列の加算(GPU) ホスト側(CPU) int main(int argc,char** argv){ … MatrixAdd<<<N, M>>>(C,A,B); … } デバイス側(GPU) __global__ void MatrixAdd(float* C,const float* A,const float* B){ int i = blockDim.x * blockIdx.x + threadIdx.x; C[i] = A[i] + B[i]; } 114 GPU側メモリ の確保 復習:典型的な実行例 GPUはCPUからの制御 で動作する。 CPU データ 転送 CUDA カーネル 実行 完了 待ち データ 転送 入力データは CPU→GPUへと転送。 結果は、 GPU→CPUと転送 GPU GPUでの演算 GPU上に常駐する プログラムはない。 115 ホスト側から呼び出すAPI cudaMalloc GPU上のDRAM(グローバルメモリ)にメモリの確保を行う cudaFree cudaMallocで取得したメモリの解放を行う cudaMemcpy CPU->GPU、GPU->GPU、GPU->CPUのメモリ転送を行う cudaDeviceSynchronize CUDAカーネルが終了するまで待つ 116 cudaMemcpy() メモリは、「ホスト」「デバイス」の二種類 enum cudaMemcpyKind cudaMemcpyHostToDevice cudaMemcpyDeviceToHost cudaMemcpyDeviceToDevice cudaMemcpyHostToHost (cudaMemcpyDefault : UVA) 117 サンプルコード(ホスト) int main() { …… 略…… int matrixSize= 256 * 100; float *A, *B, *C; cudaMalloc(&A,sizeof(float)*matrixSize); cudaMalloc(&B,sizeof(float)*matrixSize); cudaMalloc(&C,sizeof(float)*matrixSize); cudaMemcpy(A,hA, sizeof(float)*matrixSize, cudaMemcpyHostToDevice); cudaMemcpy(B,hB, sizeof(float)*matrixSize, cudaMemcpyHostToDevice); MatrixAdd<<<matrixSize/256, 256>>>(C, A, B, matrixSize); cudaDeviceSynchronize(); cudaMemcpy(hC, C, sizeof(float)*matrixSize, cudaMemcpyDeviceToHost); cudaFree(A); cudaFree(B); cudaFree(C); …… 略…… } 118 サンプルコード(デバイス) __global__ void MatrixAdd(float* C,const float* A,const float* B,const int size){ int i = blockDim.x * blockIdx.x + threadIdx.x; if( i < size){ C[i] = A[i] + B[i]; } } 119 例:RGB->YUV変換を考える 𝑌 0.299 𝑈 = −0.169 𝑉 0.500 0.587 −0.331 −0.419 0.114 0.500 −0.081 𝑅 𝐺 𝐵 121 最適化の為に理解する事 1. GPUのメモリ構造 2. スレッド(thread)構成と占有率(Occupancy) 122 最適化の為に理解する事 1. GPUのメモリ構造 2. スレッド(thread)構成と占有率(Occupancy) 123 GPUのメモリ階層 アクセスが速い SM SMEM Threads L1 Read TEX only L2 cache DRAM アクセスが遅い 124 GPU上のメモリ・キャッシュ・レジスタ GPU内部の記憶域 Cache Global Memory GPU上のDRAM。すべてのSMからアクセス 可能。 Local Memory Threadスコープのメモリ。GPU上のDRAM。 L1(Keplerのみ)、L2 スレッド内部の配列、レジスタスピル時の 記憶域。 L2 Shared Memory SM内部のメモリ。Blockスコープでアクセス。なし。手動管理のキャッ シュとして用いる場合あ 低レイテンシのRead/Write。 スレッド間のデータ共有 り。 Texture Memory テクスチャユニット経由でアクセスするメ モリ。 L1(Texture)、L2 Read-only Data Cache Read OnlyでアクセスできるGlobal Memory。L1(Texture)、L2 Constant Memory Registers 定数を収めるメモリ。ブロードキャストア クセスに特化。 SM内部のキャッシュ SM内部のレジスタ。演算可能。 なし 125 READ-ONLY(RO) CACHE SM TEX Threads Texture API SMEM L1 L2 cache Read TEX only CUDA Arrays 一般的なRead-Onlyキャッシュ として使用可能 Kepler以降 コンパイラに指示 DRAM 126 12 RO DATA CACHE 使い方 • 型修飾子: const __restrict__ を付ける __global__ kernel( int* output, const int* __restrict__ input ) input ) { ... output[idx] = ... + input[idx + delta] + ...; ... } 127 GLOBAL MEMORY SM SMEM • GPU上のメモリの中で最もポピュラーなメモリ • メモリサイズは大きく • アクセスコストは高い Threads L1 Read TEX only L2 cache Global DRAM Memory 128 コアレスアクセス 連続するスレッドは連続するメモリアクセスになるように デバイスメモリは32byte,64byte,128byteの単位でロード・ストア ※CUDA7.0現在 thread0 thread1 thread2 thread3 thread4 160 128 Device Memory thread5 ・・・・・・ 192 129 コアレスアクセス 連続するスレッドは連続するメモリアクセスになるように デバイスメモリは32byte,64byte,128byteの単位でロード・ストア ※CUDA7.0現在 thread0 thread1 thread2 thread3 thread4 160 128 Device Memory thread5 ・・・・・・ 192 130 コアレスアクセス 連続するスレッドは連続するメモリアクセスになるように デバイスメモリは32byte,64byte,128byteの単位でロード・ストア ※CUDA7.0現在 thread0 thread1 thread2 thread3 thread4 160 128 Device Memory thread5 ・・・・・・ 192 131 パディングを考慮したメモリの確保 x方向の先頭アドレスが32byteの倍数になるようにパディング 例: RGB 24byte height padding = 32 – (3*width%32) width padding 132 2次元メモリ 確保・転送API cudaMallocPitch widthバイトのメモリを、height行分、取得する 行は、パディングを考慮したpitchバイトで整列する cudaMemcpy2D cudaMallocPitchで取得したパディングを考慮したメモリ(Dst)に、 Srcのメモリ(パディングなし)をコピーする 133 サンプルコード uchar4 *src, *dImage; size_t spitch, dPitch, dPitchInPixel; // ピッチつきで、メモリをアロケート cudaMallocPitch(&dImage, &dPitch, width * sizeof(uchar4), height); dPitchInPixel = dPitch / sizeof(uchar4); // ピッチを変換しつつ、ホスト→デバイスへと、メモリ転送 cudaMemcpy2D(dImage, dPitch, src, sPitch, width * sizeof(uchar4), height,cudaMemcpyHostToDevice); 134 最適化の為に理解する事 1. GPUのメモリ構造 2. スレッド(thread)構成と占有率(Occupancy) 135 復習:一次元配列の加算 __global__ void MatrixAdd(float *A, const float *B,const float *C) { グローバルID int i = threadIdx.x + blodkDim.x * blockIdx.x; if ( i >= N || j >=N ) return; C[i][i] = A[i][j] + B[i][j]; } 総スレッド数 1ブロックあたりのスレッド数 ... MatrixAdd<<< N/128, 128>>>(A, B, C); ... 136 復習:ブロックIDとスレッドID ブロックIDとスレッドIDから、インデックス(グローバルID)を生成する インデックスを用いて各スレッドから、グローバルメモリへアクセスする index = blockDim.x * blockIdx.x + threadIdx.x; × + 2 1 6 8 Thread Block 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 0 1 2 3 0 11 4 5 12 13 14 15 16 17 0 1 2 3 4 5 2 137 ブロックIDとスレッドID(二次元) BLOCK * blockIdx.x + threadIdx.x; BLOCK =BLOCK index_x blockDim.x ・・・・・・ (M,N-2) (M,N-1) (M,N) Index_y = blockDim.y * blockIdx.y + threadIdx.y; BLOCK (1,1) BLOCK (0,0) BLOCK BLOCK (0,1) (0,2) thread thread (1,0) (1,1) thread thread BLOCK (0,1) ・・・・・・(0,0) (0,N) thread (15,15) ・・・・・・ ・・・ BLOCK (1,0) ・・・・・・ thread BLOCK ・・・・・・ (15,0) (M-1,N) ・・・・・・ BLOCK (M,0) thread ・・・・ (0,15) 138 二次元配列の加算 __global__ void MatrixAdd(float A[N][N], float *B[N][N], float *C[N][N]) { int i = threadIdx.x + blodkDim.x * blockIdx.x; int j = threadIdx.y + blodkDim.y * blockIdy.y; if ( i >= N || j >=N ) return; C[i][i] = A[i][j] + B[i][j]; } 1ブロックあたり16*16=256スレッド ... dim3 sizeBlock( 16, 16 ); dim3 numBlocks( N/sizeBlock.x, N/sizeBlock.y ); MatrixAdd<<< numBlocks, sizeBlock >>>(A, B, C); ... 139 例:RGB->YUV変換を考える 1スレッドで1pixelぶんの処理を行う ピクセルの数だけスレッドを作成 例) 1920*1080 = 2,073,600 スレッド 3840*2160 = 8,294,400 スレッド 140 例:RGB->YUV変換を考える ・・・・・・ thread7 thread6 thread5 thread4 int x = blockDim.x * blockIdx.x + threadIdx.x; int y = blockDim.y * blockIdx.y + threadIdx.y; Height if ((x < w) && (y < h)) { //Global Memory(Src)から4byte ロード uchar4 uRGB = gSrc[index]; //Global Memory(Dst)へ変換後の値を4byteストア gDst[idx] = RGB2YUV(uRGB.x, uRGB.y, uRGB.z); thread3 thread2 } thread1 thread0 Width 141 ブロックサイズの決定 x = BlockDim.x * BlockIdx.x + threadIdx.x (0<= x < width) y = BlockDim.x * BlockIdx.x + threadIdx.x (0<= y < height) グリッド・ブロックサイズの例 ) • 960 threads / block • 128 threads / block • 32 threads / block ? height width 142 ブロックサイズの決定 占有率を100%にする ブロックサイズ(ブロック辺りのスレッドの数)は少ない方が良い ブロックは横長の方が良い 143 占有率(OCCUPANCY)とは? • マルチプロセッサで同時に実行されるワープの数を同時に実行でき るワープの最大数で除算したもの 144 BLOCKDIMの決定 (占有率 から) 項目 値 最大のBlock数 / SMX 16 最大のThread数 / SMX 2048 最大のThread数 / Block 1024 SMXあたり、2048 Thread走らせたい。 Occupancy (占有率) = 100 % Occupancy = 100 % を満たす、Blockあたりのスレッド数は、 2048 Thread / 16 Block = 128 Thread / Block 2048 Thread / 8 Block = 256 Thread / Block 2048 Thread / 4 Block = 512 Thread / Block 2048 Thread / 2 Block = 1024 Thread / Block 145 BLOCKDIMの決め方(BLOCKの粒度から) Grid = 4096 Thread の実行例を考えてみる Block : 256 Thread、1024 Threadで比較 3 SMX / GPU、1 SMXあたり 1 Blockが実行可能とする SMX 0 SMX 1 SMX 2 Block Block Block Block Block Block Block Block Block Block Block Block Block Block Block Block t 256 Thread / Block SMX 0 SMX 1 SMX 2 Block Block Block Block t 1024 Thread / Block Blockサイズは小さいほうが得 → 128 Threads / Block 146 復習:カーネル実行の流れ • Giga Thread EngineがブロックをSMに割り当てる Grid Block0 Giga Thread Engine Block1 Block2 Block3 Block4 … BlockN Block4 148 復習:カーネル実行の流れ • SMの中のスケジューラがコアにスレッドを投入する Grid Block 0 Thread Thread Thread Thread 32スレッド単位で ワープを投入 投入 Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread … BlockN 149 ワープ(WARP) : 並列実行の最少単位 -ワープ(Warp) : 32 GPU スレッド 1命令を Warp (32スレッド)が、並列に処理 SIMT (Single Instruction Multiple Thread) Thread Core Core Core Core CUDA cores Thread Thread Thread Thread Thread SMX … 32 GPU Thread Warp … Warp Block Warp SW 1命令を 32並列実行 Core 150 BLOCKDIMの決め方 (SMXの構造から) Warp Scheduler x 4 : 1 clockあたり、4 Warpに対する命令発行 Blockのサイズは、 128 Thread の倍数が望ましい。 (128 Thread = 32 Thread/Warp x 4 Warp) 152 タイルは横長がよい タイルの横幅は、32(Warpの幅)の倍数がよい。 32より小さい場合、16、もしくは、8 を使う。 Thread : 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 Memory : threadIdx.x 153 RGB→Y変換時のバンド幅 : TESLA K20C blockDim.y 1 1 2 4 8 16 32 64 128 256 512 1024 1.4 2.6 4.8 8.4 13.4 17.7 20.7 20.7 20.7 20.5 19.1 2 2.8 5.2 9.6 16.7 26.3 40.4 41.7 41.6 41.0 37.6 - 4 8 blockDim.x 16 32 64 5.6 11.2 22.1 Occupancy 10.4 20.6 40.7 19.2 37.8 74.0 33.3 69.6 115.0 60.6 106.7 115.0 81.1 103.9 110.9 79.8 99.0 83.5 75.6 75.3 60.3 - blockDim.x < 8 43.9 <77.9 100 119.4 117.9 114.3 86.9 - 128 256 512 1024 78.5 119.8 119.3 115.4 87.7 %119.8 119.4 115.3 87.4 118.2 114.2 87.3 111.9 87.1 87.2 -値: バンド幅 (GB/sec) (ECC off) - Tesla K20c 154 RGB->YUV変換(ホスト) /* value、radixで割って、切り上げる */ int divRoundUp(int value, int radix) { return (value + radix – 1) / radix; } /* gridDim, blockDimを、2次元(x, y方向)に初期化 */ dim3 blockDim(128, 1); /* divRoundUp()は、切り上げの割り算 */ dim3 gridDim(divRoundUp(width, blockDim.x),divRoundUp(height, blockDim.y)); RGB2YUV<<<gridDim, blockDim>>>(dDst, dSrc, …); 155 RGB->YUV変換(デバイス) __device__ inline uchar4 rgb_2_yuv(unsigned char R, unsigned char G, unsigned char B){ float fY,fU,fY; unsigned char uY,uU,uV; fY = 0.299f * value.x + 0.587f * value.y + 0.114f * value.z; uY = (unsigned char)min(255, (int)Y); … U と Y の処理は省略 … make_uchar4(uY, uU, uV, 0); } __global__ void RGB2YUV (uchar4 *gDst, const uchar4 *gSrc, int w, int h){ int x = blockDim.x * blockIdx.x + threadIdx.x; int y = blockDim.y * blockIdx.y + threadIdx.y; if ((x < w) && (y < h)) { int index = y * width + x; //Global Memory(Src)から4byte ロード uchar4 uRGB = gSrc[index]; //Global Memory(Dst)へ変換後の値を4byteストア gDst[idx] = rgb_2_yuv(uRGB.x, uRGB.y, uRGB.z); } 156 まとめ • グローバルメモリはコアレスアクセスする • 二次元の場合はcudaMallocPitchを使う事でメモリアライメントを考慮 したメモリ確保が可能 • メモリのLoadのみの場合はRead Only Data Cacheを活用 • 占有率(Occupancy)とBlock内のスレッド構成を意識 • Blockサイズは、128が適当 (単純なカーネルの場合) • Blockの横幅は、32の倍数。無理な場合、16, 8を選択。 (4 byte / pixelの場合) 157 158 159 160 Appendix. CUDAダウンロードサイト https://developer.nvidia.com/cuda-toolkit OpenACC toolkit https://developer.nvidia.com/openacc OpenACCオンライン講座 http://info.nvidianews.com/GettingStartedwithPGIOpenACCCompiler_RegLanding-Page.html GPUコンピューティング Facebookページ https://www.facebook.com/NVIDIAGPUComputing 161 Thankyou 162 Thank you 173
© Copyright 2024 ExpyDoc