OpenACC プログラミング入門 エヌビディア合同会社 CUDA エンジニア 村上 真奈 GPU コンピューティングとは? アプリケーションコード 計算負荷が高い関数 GPU その他の逐次処理 CPUで実行 5% of Code + CPU GPU アクセラレーションの実現方法 アプリケーション GPU ライブラリ ライブラリを呼び出すだけ 簡単に高速化を実現 OpenACC ディレクティブ CUDA 既存コードにディレクティブ を挿入して高速化 重要なコードをCUDAで記述 最も自由度が高い main() { <serial code> #pragma acc kernels イリノイ大学 MRI画像再構成 //自動的にGPUで実行 { OpenACC <parallel code> } } 70倍 高速化 シンプル | 強力 | 柔軟 理研 NICAM- 大気現象シミュレーション OpenACC利用者 8000人+ 5% のコード変更で 7~8倍 高速化 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 Fortran または C言語の オリジナルコード コンパイラがコードを並列化 コンパイラへの OpenACC ヒント 並列部はGPUで 逐次処理はCPUで動作 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コアに 計算処理を分散 new OpenACC ツールキット(2015/07~) 大学関係者の方は無償で使用可能 • Linux x64/ノードロック・ライセンス • 大学関係者以外でも90日間無料トライアル可能 下記のサイトからOpenACC ツールキットをダウンロード https://developer.nvidia.com/openacc OpenACCを始める為のコンパイラ・解析 ツールなど一式が簡単にインストール OpenACC ツールキット PGIコンパイラ Free OpenACC compiler for academia NVProfプロファイラ Easily find where to add compiler directives GPUウィザード Identify which GPU libraries can jumpstart code サンプルコード http://developer.nvidia.com/openacc Learn from examples of real-world algorithms ドキュメント Quick start guide, Best practices, Forums OpenACC を始めましょう OpenACC ディレクティブ構文 C/C++ #pragma acc 指示行 [節[,]節] …] { structured block } Fortran !$acc 指示行 [節[,]節] …] { structured block } !$acc end directive 例: SAXPY (Y=A*X+Y) OpenACC OpenMP 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 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 データの移動 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上で実行される 関数 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 簡単にコンパイル 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); ... 例:ヤコビ反復法(アルゴリズム) 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])); } } for (int j = 1; j < N-1; j++) { for (int i = 1; i < M-1; i++) { A[j][i] = Anew[j][i]; } } } A(i-1,j) A(i,j) A(i+1,j) A(i,j-1) 並列領域 (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 — 複数のカーネル 簡単に解析(nvprof) 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); ... ボトルネックの可視化(nvvp) 1 サイクル GPU カーネル GPU カーネル 過剰なメモリ転送 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]; } } } 配列Aへメモリ転送 (CPU->GPU) 配列Anewへメモリ転送 (GPU->CPU) 配列Anewへメモリ転送 (CPU->GPU) 配列Aへメモリ転送 (GPU->CPU) メモリ転送のタイミングを制御 #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 性能比較 Time(sec) Speedup CPU1 OpenMP thread 26.186 -- OpenACC(最適化前) 13.638 1.92x OpenACC(最適化後) 0.773 33.88x Ubuntu 14.04LTS 64bit CPU:Intel Xeon CPU E5-1603 [email protected] x 4 GPU:Tesla K40c Thank you
© Copyright 2024 ExpyDoc