Upload
mr-vengineer
View
788
Download
2
Embed Size (px)
Citation preview
Altera SDK for OpenCL解体新書
ホストとデバイスの関係
2016.06.12(金)
@Vengineer
ホストとデバイスの関係(PCIeデバイス)
PCIeデバイスの場合は、下記のような構成になる
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−A
Kernel−B
Kernel−C
DMA
PCIe PCIe
DMA
Altera SDK for OpenCLでのBSPとは?
CPU
ホスト側のメモリ
Kernel−A
Kernel−B
Kernel−C
DMA
PCIe PCIe
DMABSPに相当する部分
デバイス側のメモリ
MIF
生成されるHDLコード
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−A
Kernel−B
Kernel−C
DMA
PCIe PCIe
DMAaocが生成するHDLコード
MIF
例題:vector addition
https://www.olcf.ornl.gov/tutorials/opencl-vector-addition/
double a[N], b[N], c[N];
vector_add( a, b, c, N );
void vector_add( double* a, double* b, double* c, const unsigned int n ){ for( unsigned int i=0 ; i<n ; i++ ) c[i] = a[i] + b[i];}
デバイス側のOpenCLコード
const char *kernelSource = #pragma OPENCL EXTENSION cl_khr_fp64 : enable _kernel void vecAdd(__global double *a, __global double *b, __global double *c, const unsigned int n) {
//Get our global thread ID int id = get_global_id(0);
//Make sure we do not go out of bounds if (id < n)
c[id] = a[id] + b[id]; }
ホスト側のCコード(メモリの割当)
h_a = (double*)malloc(bytes);h_b = (double*)malloc(bytes);h_c = (double*)malloc(bytes); d_a = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, NULL, NULL);d_b = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, NULL, NULL);d_c = clCreateBuffer(context, CL_MEM_WRITE_ONLY , bytes, NULL, NULL);
ホスト側のCコード(実行部)
err = clEnqueueWriteBuffer(queue, d_a, CL_TRUE, 0, bytes, h_a, 0, NULL, NULL);err |= clEnqueueWriteBuffer(queue, d_b, CL_TRUE, 0, bytes, h_b, 0, NULL, NULL); err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_a);err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_b);err |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &d_c);err |= clSetKernelArg(kernel, 3, sizeof(unsigned int), &n); err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &globalSize, &localSize, 0, NULL, NULL); clFinish(queue); clEnqueueReadBuffer(queue, d_c, CL_TRUE, 0, bytes, h_c, 0, NULL, NULL );
ホスト側のCコード(メモリの開放)
clReleaseMemObject(d_a); clReleaseMemObject(d_b); clReleaseMemObject(d_c);
free(h_a);free(h_b);free(h_c);
処理フロー
1) ホスト側のメモリからデバイス側のメモリにデータを移動
clEnqueueWriteBuffer2) カーネルへのパラメータ設定
clSetKernelArg3) カーネルを実行
clEnqueueTask / clEnqueuNDRangeKernel4) カーネルの終了待ち
clFinish5) デバイス側のメモリからホスト側のメモリにデータを移動
clEnqueueReadBuffer
clEnqueuWriteBuffer
ホスト側のメモリからデバイス側のメモリへデータを移動する
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−BDMA
PCIe PCIe
DMA
clSetKernelArg
カーネルへのパラメータ設定
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−BDMA
PCIe PCIe
DMA
clEnqueuTask / clEnqueueNDRageKernel
カーネルの実行
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−BDMA
PCIe PCIe
DMA
clFinish
カーネルの終了待ち
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−BDMA
PCIe PCIe
DMA
clEnqueuReadBuffer
デバイス側のメモリからホスト側のメモリへデータを移動する
CPU
ホスト側のメモリ
デバイス側のメモリ
Kernel−BDMA
PCIe PCIe
DMA
実行時間
CPUの実行時間に比べて、FPGAの実行時間が短くならないと
FPGAでのアクセレーションは意味が無い!
FPGAの実行時間 = 1) + 2) + 3) + 4) + 5)
1) ホスト側のメモリからデバイス側のメモリにデータを移動
2) カーネルへのパラメータ設定
3) カーネルを実行
4) カーネルの終了待ち
5) デバイス側のメモリからホスト側のメモリにデータを移動
ホストとデバイスの関係(SoCデバイス)
SoCデバイスの場合は、下記のような構成になる
CPU
ホスト側のメモリ
Kernel−A
Kernel−B
Kernel−C
PCIeデバイスでは必要だった、データ移動のためのDMAがない
ホスト側のCコード(メモリの割当)
cl_mem d_a = clCreateBuffer(context,CL_MEM_ALLOC_HOST_PTR, bytes, NULL, NULL);cl_mem d_b = clCreateBuffer(context,CL_MEM_ALLOC_HOST_PTR, bytes, NULL, NULL);cl_mem d_c = clCreateBuffer(context,CL_MEM_ALLOC_HOST_PTR, bytes, NULL, NULL);
// clEnqueuWriteBuffer/clEnqueueReadBufferの代わりにclEnqueueMapBufferが必要(ホスト側のメモリをマップする)
double *h_a = (double*)clEnqueueMapBuffer (queue, d_a, CL_TRUE, CL_MAP_READ, 0, bytes, 0, NULL, NULL );double *h_b = (double*)clEnqueueMapBuffer (queue, d_b, CL_TRUE, CL_MAP_READ, 0, bytes, 0, NULL, NULL );double *h_c = (double*)clEnqueueMapBuffer (queue, d_c, CL_TRUE, CL_MAP_WRITE, 0, bytes, 0, NULL, NULL );
ホスト側のCコード(実行部)
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_a);err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_b);err |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &d_c);err |= clSetKernelArg(kernel, 3, sizeof(unsigned int), &n); err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &globalSize, &localSize, 0, NULL, NULL); clFinish(queue);
ホスト側のCコード(メモリの開放)
// clEnqueueUnMapMemObjectが必要clEnqueueUnmapMemObject(queue, d_a, h_a, 0, NULL, NULL );clEnqueueUnmapMemObject(queue, d_b, h_b, 0, NULL, NULL );clEnqueueUnmapMemObject(queue, d_c, h_c, 0, NULL, NULL );
clReleaseMemObject(d_a); clReleaseMemObject(d_b); clReleaseMemObject(d_c);
// ホスト側のメモリ開放は必要なし
メモリ割当の違い
PCIeデバイス
ホスト側のメモリ割当
デバイス側のメモリ割当
....
デバイス側のメモリ開放
ホスト側のメモリ開放
SoCデバイス
デバイス側のメモリ割当
ホスト側のメモリマッピング
....
ホスト側のメモリアンマッピング
デバイス側のメモリ開放
SoCデバイスでは、CMAを使う
CPU
ホスト側のメモリ
Kernel−A
Kernel−B
Kernel−C
ホスト側のメモリの一部をデバイス側のメモリとして使用し、CPU側からアクセスできるようにマップする
Linuxをブートするときに、引数として、cma=128M のようにして事前に割り当てる
CMA(Continuous Memory Allocator)ただし、CPUからは非キャッシュ領域
参考資料)、https://aelseb.wordpress.com/2015/04/11/contiguous-memory-on-arm-and-cache-coherency/
実行時間
CPUの実行時間に比べて、FPGAの実行時間が短くならないと
FPGAでのアクセレーションは意味が無い!
FPGAの実行時間 = 1) + 2) + 3) + 4) + 5)
1) CPUがCMA領域(非キャッシュ領域)のメモリにデータを書き込む
2) カーネルへのパラメータ設定
3) カーネルを実行
4) カーネルの終了待ち
5) CPUがCMA領域(非キャッシュ領域)のメモリからデータを読み込む
おしまい