1074: openacc プログラミング入門
TRANSCRIPT
エヌビディア合同会社 CUDA エンジニア 村上 真奈
OpenACC プログラミング入門
GPU コンピューティングとは?
アプリケーションコード
+
GPU CPU5% of Code
計算負荷が高い関数
その他の逐次処理CPUで実行
GPU アクセラレーションの実現方法
アプリケーション
GPU
ライブラリ
ライブラリを呼び出すだけ簡単に高速化を実現
CUDA
重要なコードをCUDAで記述最も自由度が高い
既存コードにディレクティブを挿入して高速化
OpenACC
ディレクティブ
OpenACCシンプル | 強力 | 柔軟
イリノイ大学MRI画像再構成
70倍 高速化
理研NICAM- 大気現象シミュレーション
5% のコード変更で7~8倍高速化
main() {<serial code>#pragma acc kernels//自動的にGPUで実行
{ <parallel code>
}}
OpenACC利用者
8000人+
OpenACC ディレクティブ
Program myscience
... serial code ...
!$acc kernels
do k = 1,n1
do i = 1,n2
... parallel code ...
enddo
enddo
!$acc end kernels
...
End Program myscience
CPU GPU
Fortran または C言語のオリジナルコード
コンパイラへシンプルなヒント
コンパイラがコードを並列化
並列部はGPUで逐次処理はCPUで動作
コンパイラへのOpenACC
ヒント
OpenMP と OpenACC の比較
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);
}
printf(“pi = %f¥n”, pi/N);
}
CPU
OpenMP
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);
}
CPU GPU
OpenACC
CPUコアに計算処理を分散
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
サンプルコードLearn from examples of real-world algorithms
ドキュメントQuick start guide, Best practices, Forums
http://developer.nvidia.com/openacc
GPUウィザードIdentify which GPU libraries can jumpstart code
OpenACC を始めましょう
OpenACC ディレクティブ構文
C/C++
#pragma acc 指示行 [節[,]節] …]{ structured block }
Fortran
!$acc 指示行 [節[,]節] …]{ structured block }
!$acc end directive
例: SAXPY (Y=A*X+Y)
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);
...
omp acc データの移動
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];
}
...
saxpy(N, 3.0, x, y);
...
OpenACC 構文: parallel 指示行
• parallel : 並列に実行される領域を指示行で指定
#pragma acc parallelfor(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 kernelsfor(int i=0;i<n;i++){
a[i] = 0.0;
b[i] = 1.0;
c[i] = 2.0;
}
#pragma acc kernelsfor(int i=0;i<n;i++){
a[i] = b[i] + c[i];
}
kernel 1
kernel 2
Kernel(カーネル):
GPU上で実行される関数
簡単にコンパイルOpenMP / OpenACC
void saxpy(int n, float a,
float *x,
float *restrict y)
{
#pragma acc parallel copy(y[:n]) copyin(x[:n])
#pragma omp parallel for
for (int i = 0; i < n; ++i)
y[i] += a*x[i];
}
...
saxpy(N, 3.0, x, y);
...
$ pgcc –acc –ta=nvidia –Minfo=accel saxpy.csaxpy:
16, Generating present_or_copy(y[:n])
Generating present_or_copyin(x[:n])
Generating Tesla code
19, Loop is parallelizable
Accelerator kernel generated
19, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
例:ヤコビ反復法(アルゴリズム)
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,j) A(i+1,j)A(i-1,j)
A(i,j-1)
A(i,j+1)
並列領域 (OpenACC) Parallels と Kernels
— 並列領域を指示
Parallels
— 並列実行スタート
Kernels
— 複数のカーネル
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];
}
}
}
簡単に解析(nvprof)
OpenMP / OpenACC
void saxpy(int n, float a,
float *x,
float *restrict y)
{
#pragma acc kernels copy(y[:n]) copyin(x[:n])
#pragma omp parallel for
for (int i = 0; i < n; ++i)
y[i] += a*x[i];
}
...
saxpy(N, 3.0, x, y);
...
$ pgcc -Minfo -acc saxpy.c
saxpy:
16, Generating present_or_copy(y[:n])
Generating present_or_copyin(x[:n])
Generating Tesla code
19, Loop is parallelizable
Accelerator kernel generated
19, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
$ nvprof ./a.out ==10302== NVPROF is profiling process 10302, command: ./a.out
==10302== Profiling application: ./a.out
==10302== Profiling result:
Time(%) Time Calls Avg Min Max Name
62.95% 3.0358ms 2 1.5179ms 1.5172ms 1.5186ms [CUDA memcpy HtoD]
31.48% 1.5181ms 1 1.5181ms 1.5181ms 1.5181ms [CUDA memcpy DtoH]
5.56% 268.31us 1 268.31us 268.31us 268.31us saxpy_19_gpu
ボトルネックの可視化(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へメモリ転送(CPU->GPU)
配列Anewへメモリ転送(GPU->CPU)
配列Aへメモリ転送(GPU->CPU)
メモリ転送のタイミングを制御データ制御ディレクティブ
copyin (CPUGPU)
copyout (CPUGPU)
copy
create
present
pcopyin
pcopyout
pcopy
pcreate
#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];
}
}
}
性能比較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