パイプライン実行および CudaScheduler の実装
+CudaScheduler の実装および OpenCL, CUDA でのパイプライン実行
PS3 および Linux、Mac OS X 上で動く並列プログラミングフレームワーク、 Cerium の開発・改良を行っている @@ -59,8 +59,25 @@ この質問があったので CUDA バージョンを3週間で実装した
# HG changeset patch
# User Shohei KOKUBO
PS3 および Linux、Mac OS X 上で動く並列プログラミングフレームワーク、
Cerium の開発・改良を行っている
@@ -59,8 +59,25 @@
この質問があったので CUDA バージョンを3週間で実装した
+ CPU と GPU はメモリ空間が異なるのでメモリの共有ができない。データにアクセスするためには空間ごとコピーする必要がある。この転送部分がネックになる。
+
+ GPGPU ではデータ転送が頻繁に起こるような Task は並列度が低くなる。
+ パイプライン実行および CudaScheduler の実装
+ CudaScheduler の実装および OpenCL, CUDA でのパイプライン実行
GPU のアーキテクチャ
+
+
+
+
+
+
+
+
+
+ CUDA と OpenCL の対応(1/2)
+ Porting OpenCL to CUDA(1/2)
用語
@@ -92,40 +109,20 @@
CUDA と OpenCL の対応(2/2)
-
-
-
+ オブジェクト
-
-
- OpenCL
- CUDA
-
-
- cl_device_id
- CUdevice
-
-
- cl_context
- CUcontext
-
-
- cl_program
- CUmodule
-
-
- cl_kernel
- CUfunction
-
-
- cl_mem
- CUdeviceptr
-
-
-
- cl_command_queue
- CUstream
- Porting OpenCL to CUDA(2/2)
+ OpenCL
+
+ cl_mem memA = clCreateBuffer(context, CL_MEM_READ_WRITE,
+ sizeof(float), NULL, &ret);
+ clEnqueueWriteBuffer(command_queue, memA, CL_FALSE, 0,
+ sizeof(float), A, 0, NULL, NULL);
+
+ CUDA
+
+ CUdeviceptr devA;
+ cuMemAlloc(&devA, sizeof(float));
+ cuMemcpyHtoDAsync(devA, A, sizeof(float), stream);
+
CUDA による GPGPU への対応
@@ -148,46 +145,26 @@
- |
- - TaskManager と各 Threads は mail によって同期を取っている。 - -- 実行するデバイスに対応した Threads が Task を受け取り、並列実行する。 - - |
-
- 生成した Task は TaskManager で依存関係をチェックし、依存関係が解消されたあと、各 Scheduler に転送される。 + 生成した Task は TaskManager で依存関係をチェックされる。依存関係がないと ActiveTaskList に移され、実行するデバイスの Scheduler に転送される。
- |
- - CPU と GPU はメモリ空間が異なるのでメモリの共有ができない。データにアクセスするためには空間ごとコピーする必要がある。この転送部分がネックになる。 - -- GPGPU ではデータ転送が頻繁に起こるような Task は並列度が低くなる。 - |
-
+ Scheduler に転送されるとそこで並列実行される。Task が終了すると同期キューである mail を使用し、TaskManager に終了した Task を通知し、再び依存関係をチェックする。 +
+ OpenCL の Command Queue は queue を生成するとき、プロパティを設定することで単一の queue でも Operation を並列実行するようにできる。 + しかし、ほとんどのデバイスでこのプロパティは使用することができない。 +
+Stream に投入された Operation は投入された順序で実行されることが保証されている
@@ -199,17 +176,17 @@
- 同一の kernel を生成し、各 kernel に対して ID を割り振る。
+ 同一の kernel を複数生成し、各 kernel に対して index を割り振る。
- kernel に割り振られた ID から担当する範囲を求め、処理を行う。
+ 割り振られた index から自分が担当する範囲(GPU 上でのメモリ領域)を求め、各 kernel が並列に処理を行う。
- ID は組み込み変数から算出することができる。
+ CUDA では index は組み込み変数から算出することができる。OpenCL では get_global_id という API を用いることで index を取得できる。
+ iterate を使用するとデータ並列を行う Task として登録される。この時点で tasklist にパラメータが設定され、TaskManager から Scheduler に送られる。
+
CudaScheduler は受け取った tasklist をもとに GPU の制御を行う 実験環境 実験環境CUDA におけるデータ並列
Cerium におけるデータ並列のための新しい API
+ Many Core CPU によるデータ並列実行のための新しい API
@@ -225,40 +202,49 @@
Cerium における CUDA でのデータ並列
+ Cerium における CUDA でのデータ並列(1/2)
+
+ void
+ HTask::iterate(long x, long y, long z) {
+ this->flag.dim_count = 1;
+ TaskList *tl = (TaskList*)rbuf;
+ tl->self = this;
+ tl->set_last(last);
+ tl->dim=3;
+ tl->x=x;
+ tl->y=y;
+ tl->z=z;
+ mimpl->spawn_task(this);
+ }
+ Cerium における CUDA でのデータ並列(2/2)
tasklist は制御に必要なパラメータを持っている
+ TaskPtr nextTask = tasklist->tasks;
+ ListElement* input_buf = nextTask->inData;
+ cuMemcpyHtoDAsync(memin, input_buf->addr, input->buf->size, stream);
+
+ kernel の実行
cuLaunchKernel(function, tasklist->x, tasklist->y, tasklist->z,
1, 1, 1, 0, stream[cur], kernelParam, NULL);
-
-
-
taskList の持つメンバ変数
-
-
- x
- x 座標のブロック数
-
-
- y
- y 座標のブロック数
-
-
-
- z
- z 座標のブロック数
- WordCount の測定
+ WordCount の測定(1/2)
+
+
@@ -286,10 +272,6 @@
46.74s
-
- OpenCL(Data Parallel)
- 0.50s
-
@@ -297,6 +279,43 @@
CUDA(no pipeline)
55.71s
CUDA(pipeline)
53.30s
+
+
+
+
+
+
+ 100MB のテキストに対するタスク並列で wordcount
+
+
+ CPU : 2*2.66GHz 6-CoreIntel Xeon
+ GPU : NVIDIA Quadro K5000 4096MB
+ Compiler : Apple LLVM version 5.0(clang-500.2.79)(based on LLVM 3.3svn)
+
+ 結果
+ パイプラインありの方が OpenCL では1.03倍、CUDA では1.04倍の性能向上が見られた
+ しかし、CPU に比べてまだ60倍以上遅い
+ WordCount の測定(2/2)
+
+
+
+
@@ -418,6 +436,27 @@
60inch4Kくらいの買ってください。
+
+
+
+
+
+
+ Time
+
+
OpenCL(Data Parallel)
+ 0.50s
+
CUDA(Data Parallel)
0.73s
@@ -305,7 +324,7 @@
-
100MB のテキストファイルに対する wordcount
+ 100MB のテキストに対するデータ並列で wordcount
@@ -315,8 +334,7 @@
Compiler : Apple LLVM version 5.0(clang-500.2.79)(based on LLVM 3.3svn)
結果
- パイプラインありの方が少しだけ性能が向上した
- データ並列による実行では、OpenCL 版は 1 CPU より速く、CUDA 版はほぼ同等という結果になった
+ データ並列による実行ではタスク並列の実行に比べ、OpenCL は93倍、CUDA では73倍の性能向上が見られた
kernel の差異
+ OpenCL
+
+ __kernel void
+ multiply(__global float* A, __global float* B, __global float* C) {
+ long i = get_global_id(0);
+ C[i] = A[i] * B[i];
+ }
+
+ CUDA
+
+ extern "C" {
+ __global__ void
+ multiply(float* A, float* B, float* C) {
+ long i = blockIdx.x*blockDim.x*+threadIdx.x;
+ C[i] = A[i] * B[i];
+ }
+ }
+
+