天天看點

OpenCL 第5課:向量相加

OpenCL程式分為兩個部份,一部份是核心代碼,負責具體算法。另一部份是主程式負責初始化OpenCL和準備資料。主程式加載核心代碼,并按照即定方法進行運算。

核心代碼可以寫在主程式裡面,也可以寫在另一個文本檔案裡,有點像DX中的HLSL和OPENGL裡的GLSL。哈哈,明白意思就行了。我們用第一種方法,把代碼跟源程式分開寫。

調用OpenCL大至分7個步驟

1:初始化OpenCL

2:建立上下文裝置

3:建立指令隊列

4:建立資料緩沖區

5:将資料上傳到緩沖區

6:加載編譯代碼,建立核心調用函數

7:設定參數,執行核心

8:讀回計算結果。

下面我們通過一個向量相加的程式來了解OpenCL 。有A,B兩個四維向量,相加後值存在C向量裡。OpenCL會根據使用者提供的維數,将向量分解成多個任務分發給多個CPU計算。

源碼分兩部份

(一)vecadd.cl核心代碼。

?

1 2 3 4 5 6 7 8

__kernel

void

vecAdd(__global

int

* A,

__global

int

* B,

__global

int

* C)

{

//擷取目前工作項所在位置(線程索引号)

int

idx = get_global_id(0);

C[idx] = A[idx] + B[idx];

}

__kernel 指明這是一個OpenCL核心,__global 說明指針指向的是全局的裝置記憶體空間,其它的就是C語言的函數的文法。kernel必須傳回空類型。

(二)main.cpp代碼

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

#include <iostream>

#include <stdio.h>

#include <string.h>

#include <string>

#include <CL/cl.h>//包含CL的頭檔案

using

namespace

std;

//四維向量

#define elements 4

//從外部檔案擷取cl核心代碼

bool

GetFileData(

const

char

* fname,string& str)

{

FILE

* fp =

fopen

(fname,

"r"

);

if

(fp==NULL)

{

printf

(

"no found file\n"

);

return

false

;

}

int

n=0;

while

(

feof

(fp)==0)

{

str +=

fgetc

(fp);

}

return

true

;

}

int

main()

{

//先讀外部CL核心代碼,如果失敗則退出。

//代碼存buf_code裡面

string code_file;

if

(

false

== GetFileData(

"vecadd.cl"

,code_file))

return

0;

char

* buf_code =

new

char

[code_file.size()];

strcpy

(buf_code,code_file.c_str());

buf_code[code_file.size()-1] = NULL;

//聲明CL所需變量。

cl_device_id device;

cl_platform_id platform_id = NULL;

cl_context context;

cl_command_queue cmdQueue;

cl_mem bufferA,bufferB,bufferC;

cl_program program;

cl_kernel kernel = NULL;

//我們使用的是一維向量

//設定向量大小(維數)

size_t

globalWorkSize[1];

globalWorkSize[0] = elements;

cl_int err;

int

* buf_A =

new

int

[elements];

int

* buf_B =

new

int

[elements];

int

* buf_C =

new

int

[elements];

size_t

datasize =

sizeof

(

int

) * elements;

buf_A[0] = 1;

buf_A[1] = 2;

buf_A[2] = 3;

buf_A[3] = 4;

buf_B[0] = 5;

buf_B[1] = 6;

buf_B[2] = 7;

buf_B[3] = 8;

//step 1:初始化OpenCL

err = clGetPlatformIDs(1,&platform_id,NULL);

if

(err!=CL_SUCCESS)

{

cout<<

"clGetPlatformIDs error"

<<endl;

return

0;

}

//這次我們隻用CPU來進行并行運算,當然你也可以該成GPU

clGetDeviceIDs(platform_id,CL_DEVICE_TYPE_CPU,1,&device,NULL);

//step 2:建立上下文

context = clCreateContext(NULL,1,&device,NULL,NULL,NULL);

//step 3:建立指令隊列

cmdQueue = clCreateCommandQueue(context,device,0,NULL);

//step 4:建立資料緩沖區

bufferA = clCreateBuffer(context,

CL_MEM_READ_ONLY,

datasize,NULL,NULL);

bufferB = clCreateBuffer(context,

CL_MEM_READ_ONLY,

datasize,NULL,NULL);

bufferC = clCreateBuffer(context,

CL_MEM_WRITE_ONLY,

datasize,NULL,NULL);

//step 5:将資料上傳到緩沖區

clEnqueueWriteBuffer(cmdQueue,

bufferA,CL_FALSE,

0,datasize,

buf_A,0,

NULL,NULL);

clEnqueueWriteBuffer(cmdQueue,

bufferB,CL_FALSE,

0,datasize,

buf_B,0,

NULL,NULL);

//step 6:加載編譯代碼,建立核心調用函數

program = clCreateProgramWithSource(context,1,

(

const

char

**)&buf_code,

NULL,NULL);

clBuildProgram(program,1,&device,NULL,NULL,NULL);

kernel = clCreateKernel(program,

"vecAdd"

,NULL);

//step 7:設定參數,執行核心

clSetKernelArg(kernel,0,

sizeof

(cl_mem),&bufferA);

clSetKernelArg(kernel,1,

sizeof

(cl_mem),&bufferB);

clSetKernelArg(kernel,2,

sizeof

(cl_mem),&bufferC);

clEnqueueNDRangeKernel(cmdQueue,kernel,

1,NULL,

globalWorkSize,

NULL,0,NULL,NULL);

//step 8:取回計算結果

clEnqueueReadBuffer(cmdQueue,bufferC,CL_TRUE,0,

datasize,buf_C,0,NULL,NULL);

//輸出計算結果

cout<<

"["

<<buf_A[0]<<

","

<<buf_A[1]<<

","

<<buf_A[2]<<

","

<<buf_A[3]<<

"]+["

<<buf_B[0]<<

","

<<buf_B[1]<<

","

<<buf_B[2]<<

","

<<buf_B[3]<<

"]=["

<<buf_C[0]<<

","

<<buf_C[1]<<

","

<<buf_C[2]<<

","

<<buf_C[3]<<

"]"

<<endl;

//釋放所有調用和記憶體

clReleaseKernel(kernel);

clReleaseProgram(program);

clReleaseCommandQueue(cmdQueue);

clReleaseMemObject(bufferA);

clReleaseMemObject(bufferB);

clReleaseMemObject(bufferC);

clReleaseContext(context);

delete

buf_A;

delete

buf_B;

delete

buf_C;

delete

buf_code;

return

0;

}

運算結果:

 [1,2,3,4] + [5,6,7,8] = [6,8,10,12]