2015年度GPGPU実践基礎工学 第15回 GPGPU開発環境(OpenCL)
-
Upload
- -
Category
Engineering
-
view
387 -
download
0
Transcript of 2015年度GPGPU実践基礎工学 第15回 GPGPU開発環境(OpenCL)
第15回 GPGPU開発環境(OpenCL)
長岡技術科学大学 電気電子情報工学専攻 出川智啓
計算機名称 アクセラレータ 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
Green500(2015, Nov.) TOP3の計算機がそれぞれ異なるアクセラレータを搭載
インターネットのサービス提供に利用されてる(と思われる)計算機が大量にランクイン
GPGPU実践基礎工学2 2015/12/16
http://www.green500.org/lists/green201511
今回の内容
GPGPU実践基礎工学
OpenCL 背景
OpenCLの仕様
grouseで利用できるOpenCL環境の確認
grouseのOpenCL環境から利用できるデバイスの確認
サンプルプログラム
3 2015/12/16
OpenCLとは
Open Computing Language 不均一な並列環境向けの標準化フレームワーク
ロイヤリティ不要のオープンスタンダード
処理するハードウェアに依存しない
CPU, GPU, Cell等のプロセッサで動作
統一されたAPIによる制御が可能
OpenCL Working Groupによって策定
Appleにより提案
AMDが自社GPUの標準開発環境として採用
GPGPU実践基礎工学4 2015/12/16
OpenCLが登場した背景
GPGPU実践基礎工学
均一な計算機環境から不均一な計算機環境への移行 マルチコアCPU CPU+GPU Cell/B.E.
計算機環境の多様化 ソフトウェアの移植が困難
ハードウェア構造,命令構文の違い
ソフトウェア・ハードウェアの変更と移植性の両立 ソフトウェアの移植性を維持
ハードウェアの変更への対応
プロセッサの種類に依存しない開発環境
5 2015/12/16
OpenCLの仕様
GPGPU実践基礎工学
ハードウェアの抽象化
制御用プロセッサ+演算用プロセッサ
CPU(1コア)+CPU(その他のコア)
CPU+GPU PPE+SPE (Cell/B.E.) PowerPC Processor Element(PPE)+Synergistic Processor Element(SPE)
不均一な計算環境を想定
演算用プロセッサ上でOSが動作していないと想定
制御用プロセッサから演算プログラムを起動
演算用プログラムをOpenCL,制御用プログラムをC言語で作成
6 2015/12/16
OpenCLの仕様
GPGPU実践基礎工学
二つの仕様を標準化
OpenCL C言語仕様
演算用プロセッサで動作するプログラムの記述方法
C言語の拡張
OpenCLランタイムAPI仕様
制御用プロセッサが利用するAPI(C言語からの関数呼出)
OpenCL C言語で作成されたプログラムを呼び出し,演算用プロセッサで並列実行
7 2015/12/16
OpenCLプログラムの構成
GPGPU実践基礎工学
ホストプログラム(拡張子は.cpp) OpenCLランタイムAPIを利用してC/C++言語で記述
カーネル(拡張子は.cl) OpenCLデバイスで動作するプログラム
OpenCL C言語で記述
OpenCLデバイス ホスト
CPU
メモリ
CUDA Core
メモリ
ホストプログラムカーネル 呼出
8 2015/12/16
OpenCLプログラムの構成
GPGPU実践基礎工学
ホストプログラムからカーネルを読み込んで実行
オンラインコンパイル方式を採用
プログラム実行時にOpenCLランタイムライブラリがカーネルをコンパイル,実行(Just in Timeコンパイル)
デバイスに依存せずにプログラムを配布可能
ホストプログラム カーネル
OpenCLランタイムライブラリ
ホスト OpenCLデバイス
コンパイル
カーネル読込
実行
9 2015/12/16
実行
OpenCL環境の確認
使用(ログイン)している計算機環境でOpenCLが有効になっているかの確認
grouseにはNVIDIA社が提供するOpenCL環境が存在
コンパイル
nvcc ‐lOpenCL ソースファイル名.cpp ソースファイルの拡張子は.cuではなく.cpp
2015/12/16GPGPU実践基礎工学10
$ ls /etc/OpenCL/vendorsnvidia.icd
OpenCLから利用できるデバイスの確認
GPGPU実践基礎工学11
プラットフォーム
複数の会社がOpenCL(プラットフォーム)を提供
AMD, Apple, IBM, Intel, NVIDIAなど(約10社が提供)
複数のプラットフォームをインストール可能
利用している計算機に複数のプラットフォームがあれば,その中から一つを指定
デバイス
プラットフォームで利用するデバイスを指定
指定したプラットフォームから利用できるデバイスと利用できないデバイスがある
grouseで利用するNVIDIAのOpenCLプラットフォームではCPUが利用できない
2015/12/16
OpenCLから利用できるデバイスの確認
2015/12/16GPGPU実践基礎工学12
#include<stdio.h>#include<CL/cl.h>#define MAX_PLATFORMS (1)#define MAX_DEVICES (4)#define INFO_SIZE (4096)int main(){
cl_uint num_platform, num_device;cl_platform_id platform_ids[MAX_PLATFORMS];cl_device_id device_ids[MAX_DEVICES];char device_info[INFO_SIZE];
clGetPlatformIDs(MAX_PLATFORMS, platform_ids, &num_platform);printf("number of platforms : %d ¥n", (int)num_platform);for(int pID=0; pID<(int)num_platform; pID++){
printf("platform id : %d¥n", pID);
clGetDeviceIDs(platform_ids[pID], CL_DEVICE_TYPE_ALL, MAX_DEVICES, device_ids, &num_device);printf("device type all¥t : %d ¥n", num_device);for(int dID=0; dID<(int)num_device; dID++){
printf("device id : %d¥n", dID);
clGetDeviceInfo(device_ids[dID], CL_DEVICE_NAME, INFO_SIZE, device_info, NULL);printf("device name¥t : %s ¥n", device_info);clGetDeviceInfo(device_ids[dID], CL_DEVICE_VENDOR, INFO_SIZE, device_info, NULL);printf("device vendor¥t : %s ¥n", device_info);
}}return 0;
} opencl_device.cpp
OpenCLから利用できるデバイスの確認
GPGPU実践基礎工学13
clGetPlatformIDs 利用できるOpenCLのプラットフォーム(環境)数を取得
clGetPlatformIDs(取得するプラットフォームの上限,プラットフォーム情報,プラットフォーム数);
clGetDeviceIDs 指定のOpenCLプラットフォームから利用できるデバイスの数を取得
clGetDeviceIDs(プラットフォームID, デバイスの種類,取得するデバイスの上限,デバイス情報,デバイス数);
clGetDeviceInfo プラットフォームから利用できるデバイス数の詳細情報を取得
clGetDeviceInfo(デバイスID, 取得する情報,保存先のchar型配列のサイズ,保存先のchar型配列, 取得した文字数);
2015/12/16
OpenCLから利用できるデバイスの確認
GPGPU実践基礎工学14
実行結果number of platforms : 1platform id : 0device type all : 4device id : 0device name : Tesla M2050device vendor : NVIDIA Corporationdevice id : 1device name : Tesla M2050device vendor : NVIDIA Corporationdevice id : 2device name : Tesla M2050device vendor : NVIDIA Corporationdevice id : 3device name : Tesla M2050device vendor : NVIDIA Corporation
←利用できるプラットフォームは1個
←プラットフォームID0から利用できるデバイスは4個
2015/12/16
プラットフォームID0から利用できるデバイスの情報(名前とメーカー)
OpenCLプログラムの決まり事
GPGPU実践基礎工学15
OpenCLのヘッダをインクルードする
#include<CL/cl.h>
OpenCLで利用する変数の型や関数はclで始まる
例
cl_uint型 clGetPlatformIDs()
コンパイル時にはOpenCLのライブラリへリンクする
‐lOpenCLオプションを付与
2015/12/16
ハードウェアモデル
GPGPU実践基礎工学16
OpenCLデバイスは複数の演算ユニットから構成
演算ユニットは複数のプロセッシングエレメント(PE)から構成
GPUとの対応
OpenCLデバイス = GPU 演算ユニット = SM PE = CUDA Core
OpenCLデバイス
PE PE PEPE
・・・
PE PE PEPE
演算ユニット
PE PE PEPE
PE PE PEPE
演算ユニット
2015/12/16
並列プログラミングモデル
GPGPU実践基礎工学17
データ並列プログラミング
タスク並列プログラミングモデル
ホストプログラム コマンドキュー
処理
OpenCLデバイス
演算ユニット
演算ユニット
演算ユニット
処理
処理
処理
インデックス空間
1
2
3
データ並列実行指示
データ並列実行
ホストプログラム コマンドキュー
OpenCLデバイス
演算ユニット
演算ユニット
演算ユニット
処理
処理
処理
タスク並列実行指示
タスク並列実行
処理
処理
処理
インデックス空間1
インデックス空間1
インデックス空間1
2015/12/16
並列プログラミングモデル
GPGPU実践基礎工学18
データ並列実行時に演算ユニットとPEに固有のIDを付与
インデックス空間の展開
演算ユニットで実行する単位
ワークグループ
PEで実行する単位
ワークアイテム
OpenCLデバイス
PE
PE
PE演算ユニット
ワークアイテムID 0
ワークアイテムID 1
ワークアイテムID 2
ワークグル
ープID 0
PE
PE
PE演算ユニット
ワークアイテムID 0
ワークアイテムID 1
ワークアイテムID 2
ワークグル
ープID 1
グローバルアイテム数 6ローカルアイテム数 3
2015/12/16
メモリモデル
GPGPU実践基礎工学19
グローバルメモリ
全てのワークアイテムから読み書きできるメモリ
コンスタントメモリ
全てのワークアイテムから読み込みだけができるメモリ
ローカルメモリ
ワークグループ内のワークアイテムが共有できるメモリ
プライベートメモリ
ワークアイテム専用のメモリ
2015/12/16
メモリモデル
GPUとの対応
グローバルメモリ=グローバルメモリ
コンスタントメモリ=コンスタントメモリ
ローカルメモリ=共有メモリ
プライベートメモリ=レジスタ
GPGPU実践基礎工学20
全てのワークアイテムがデータを共有
ワークグループ内でデータを共有
コンスタントメモリ
OpenCLデバイス
P P P P
PE PE PE PE
ローカルメモリ
演算ユニット
P P P P
PE PE PE PE
ローカルメモリ
演算ユニット
グローバルメモリ
各ワークアイテムが個別のデータを保有
2015/12/16
プライベートメモリ
プロセッシングエレメント
カーネルの作成
GPGPU実践基礎工学21
関数に修飾子__kernelを付加
引数となる変数にアドレス修飾子を付加
グローバルメモリに存在する場合 __global コンスタントメモリに存在する場合 __constant ローカルメモリに存在する場合 __local プライベートメモリに存在する場合 __private
一つのワークアイテムが実行する内容を記述
ワークアイテムのIDを取得
IDを基に並列処理を実行
2015/12/16
ホストコード
2015/12/16GPGPU実践基礎工学22
カーネルを実行するために大量の下準備が必要
1.実行するプラットフォームの特定
2.利用するデバイスの決定*
3.コンテキストの作成
4.コマンドキューの作成
5.メモリオブジェクトの作成*
6.カーネルの読み込み
7.プログラムオブジェクトの作成
8. カーネルのコンパイル
9. カーネルオブジェクトの作成
10. カーネル引数の設定
11. コマンドキューへの投入(カーネルの実行*)
12. メモリオブジェクトから結果を読み出し*
13. オブジェクトの解放*
*GPUと共通
ベクトル和C=A+Bの計算
配列A, B, Cで参照する配列要素番号iが同じ
各スレッドがある配列添字iを処理
1スレッドが1要素の計算を実行
GPGPU実践基礎工学23 2015/12/16
・・・
・・・
・・・c[i]
a[i]
b[i]
+ + + + + +
スレッド0 スレッド2スレッド1 スレッド3
CUDAの並列化階層と情報の参照
N=8, <<<2, 4>>>で実行
c[i]
a[i]
b[i]
+ + + + + + + +
gridDim.x=2
blockIdx.x=0 blockIdx.x=1
blockDim.x=4blockDim.x=4threadIdx.x=
0 1 2 3 0 1 2 3
threadIdx.x=
2015/12/16GPGPU実践基礎工学24
i= 0 1 2 3 4 5 6 7= blockIdx.x*blockDim.x + threadIdx.x
CUDA Cと同じ条件で並列実行
c[i]
a[i]
b[i]
+ + + + + + + +
get_num_groups()
get_group_id() get_group_id()
get_local_size()get_local_size()get_local_id()
0 1 2 3 0 1 2 3
get_local_id()
2015/12/16GPGPU実践基礎工学25
i= 0 1 2 3 4 5 6 7= get_global_id()
OpenCLの並列化階層と情報の参照
逐次プログラム
2015/12/16GPGPU実践基礎工学26
#include<stdlib.h>#define N (1024*1024)#define Nbytes (N*sizeof(float))
void init(float *a, float *b, float *c){
int i;
for(i=0; i<N; i++){a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;
}}
void add(float *a, float *b, float *c){
int i;
for(i=0; i<N; i++)c[i] = a[i] + b[i];
}
int main(void){float *a,*b,*c;
a = (float *)malloc(Nbytes);b = (float *)malloc(Nbytes);c = (float *)malloc(Nbytes);
init(a,b,c);add(a,b,c);
free(a);free(b);free(c);return 0;
}
CUDA Cによる並列実行プログラム
2015/12/16GPGPU実践基礎工学27
#define N (1024*1024)#define Nbytes (N*sizeof(float))
__global__ void init(float *a,float *b, float *c){
int i = blockIdx.x*blockDim.x+ threadIdx.x;
a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;
}
__global__ void add(float *a,float *b, float *c){
int i = blockIdx.x*blockDim.x+ threadIdx.x;
c[i] = a[i] + b[i];}
int main(void){float *a,*b,*c;
cudaMalloc((void **)&a, Nbytes);cudaMalloc((void **)&b, Nbytes);cudaMalloc((void **)&c, Nbytes);
init<<< N/256, 256>>>(a,b,c);add<<< N/256, 256>>>(a,b,c);
cudaFree(a);cudaFree(b);cudaFree(c);return 0;
}
CUDA CのKernel
2015/12/16GPGPU実践基礎工学28
__global__ void init(float *a, float *b, float *c){int i = blockIdx.x*blockDim.x + threadIdx.x;
a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;
}
__global__ void add(float *a, float *b, float *c){int i = blockIdx.x*blockDim.x + threadIdx.x;
c[i] = a[i] + b[i];
}
OpenCLのカーネルコード
2015/12/16GPGPU実践基礎工学29
__kernel void init(__global float *a, __global float *b, __global float *c){int i = get_global_id(0);
a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;
}
__kernel void add(__global float *a, __global float *b, __global float *c){int i = get_global_id(0);
c[i] = a[i] + b[i];
}
clvectoradd.cl
OpenCLのホストプログラム
2015/12/16GPGPU実践基礎工学30
#include<stdio.h>#include<stdlib.h>#include<CL/cl.h>
#define N (1024*1024)#define NBytes (N*sizeof(float))#define MAX_SOURCE_SIZE (0x100000)
int main(){
cl_device_id device_id = NULL;cl_platform_id platform_id = NULL;cl_context context = NULL;cl_command_queue command_queue = NULL;cl_uint num_platform;cl_uint num_device;cl_int stat;
cl_program program = NULL;cl_kernel kernel[2] = {NULL,NULL};
cl_mem a = NULL;cl_mem b = NULL;cl_mem c = NULL;
float *host_c=(float *)malloc(Bytes);
FILE *fp;char *filename = "./add.cl";char *source_str;size_t source_size;
//6.カーネルのソースコードを読み込みif( (fp = fopen(filename,"r"))==NULL ){
printf("error¥n");return 1;
}source_str = (char *)malloc
(MAX_SOURCE_SIZE);source_size = fread(source_str,1,
MAX_SOURCE_SIZE, fp);fclose(fp);
clvectoradd.cpp
OpenCLのホストプログラム
2015/12/16GPGPU実践基礎工学31
//1.実行するプラットフォームの決定clGetPlatformIDs(1, &platform_id,
&num_platform);
//2.実行するデバイスの決定clGetDeviceIDs(platform_id,
CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &num_device);
//3.OpenCLコンテキストの作成context = clCreateContext(NULL, 1,
&device_id, NULL, NULL, &stat);
//4.コマンドキューの作成command_queue = clCreateCommandQueue
(context, device_id, 0, &stat);
//5.メモリバッファオブジェクトの作成a = clCreateBuffer(context, CL_MEM_READ_WRITE, NBytes, NULL, &stat);
b = clCreateBuffer(context, CL_MEM_READ_WRITE, NBytes, NULL, &stat);
c = clCreateBuffer(context, CL_MEM_READ_WRITE, NBytes, NULL, &stat);
//7.プログラムオブジェクトの作成program = clCreateProgramWithSource(context, 1, (const char **)&source_str,
(const size_t *)&source_size, &stat);
//8.カーネルのコンパイルclBuildProgram(program,1, &device_id,
NULL, NULL, NULL);
//9.カーネルオブジェクトの作成kernel[0] = clCreateKernel(program,
"init", &stat);kernel[1] = clCreateKernel(program,
"add", &stat);
clvectoradd.cpp
OpenCLのホストプログラム
2015/12/16GPGPU実践基礎工学32
//10.カーネル引数の設定clSetKernelArg(kernel[0], 0,
sizeof(cl_mem), (void *)&a);clSetKernelArg(kernel[0], 1,
sizeof(cl_mem), (void *)&b);clSetKernelArg(kernel[0], 2,
sizeof(cl_mem), (void *)&c);clSetKernelArg(kernel[1], 0,
sizeof(cl_mem), (void *)&a);clSetKernelArg(kernel[1], 1,
sizeof(cl_mem), (void *)&b);clSetKernelArg(kernel[1], 2,
sizeof(cl_mem), (void *)&c);
//アイテムの数(並列度)の設定size_t global_item_size = N;size_t local_item_size = 256;
//11.カーネルの実行clEnqueueNDRangeKernel(command_queue,
kernel[0], 1, NULL, &global_item_size, &local_item_size, 0, NULL, NULL);
clEnqueueNDRangeKernel(command_queue, kernel[1], 1, NULL, &global_item_size,
&local_item_size, 0, NULL, NULL);
//12.メモリオブジェクトから結果を読み出しclEnqueueReadBuffer(command_queue, c,
CL_TRUE,0,NBytes,host_c,0,NULL,NULL);
//結果の表示for(int i=0; i<N; i++)
printf("%d, %f¥n",i,host_c[i]);
clvectoradd.cpp
OpenCLのホストプログラム
2015/12/16GPGPU実践基礎工学33
//13.オブジェクトの解放clFlush (command_queue);clFinish(command_queue);clReleaseKernel(kernel[1]);clReleaseKernel(kernel[0]);clReleaseProgram(program);clReleaseMemObject(a);clReleaseMemObject(b);clReleaseMemObject(c);clReleaseCommandQueue(command_queue);clReleaseContext(context);
free(host_c);free(source_str);
return 0;
}
clvectoradd.cpp
性能比較
2015/12/16GPGPU実践基礎工学34
配列の要素数220
CUDA CとOpenCLで同じ傾向
1ブロックあたりのスレッド数が増加すると性能が向上
スレッド数を多くしすぎると性能が低下
OpenCLはCUDAより実行効率の悪い実行ファイルを生成?
Number of Threads/Block
Processing time [ms]CUDA C OpenCL
32 0.276 0.43164 0.169 0.301128 0.128 0.268256 0.119 0.257512 0.120 0.2581024 0.151 0.279
GPU/CUDAとOpenCLの対応
GPU
SM
CUDA Core
GPU
GPGPU実践基礎工学35
CUDA
Grid
Block
Thread
OpenCL
OpenCLデバイス
ワークグループ
ワークアイテム
OpenCL
グローバルメモリコンスタントメモリ
ローカルメモリ
プライベートメモリ
OpenCL
OpenCLデバイス
演算ユニット
プロセッシングエレメント
GPU/CUDA
グローバルメモリコンスタントメモリ
共有メモリ
レジスタ
ハードウェア構成 並列化階層 メモリ階層
ワークグループID
ワークアイテムID(ローカルID)
blockIdx
threadIdx
2015/12/16