Parallel/OpenCL
Hello OpenCL
Houkibosi
2013. 10. 8. 17:02
// int val[2] = {1,2}를 입력으로 받아서 val = { 2,1 } 로 값을 변환하여 주는 예제프로그램이다.
// test platform : Visual Studio 2008 + ati stream sdk beta2.0
#include <stdio.h>
#include <stdlib.h>
#include <CL/cl.h>
// error check macro : utility
#define CHECK_CL_ERROR(retcode, msg, exit_label) { \
if (retcode != CL_SUCCESS) { \
printf(msg); printf("\n"); \
goto exit_label; \
}\
}
int main()
{
cl_int input[2] = {1, 2};
cl_int output[2] = {};
cl_context context = 0;
cl_device_id * devices = NULL;
cl_mem inputBuffer = 0;
cl_mem outputBuffer = 0;
cl_command_queue cmd_queue = 0;
cl_program program = 0;
cl_kernel kernel = 0;
cl_int status = 0;
size_t num_devices = 0;
// 1st. context를 만든다. (예제는 CPU)
// GPU나 다른 context를 만들려면 플랫폼정보를 읽어들여서 지원범위를 보고 지원한다면 결정해주면된다.
context = clCreateContextFromType( 0, CL_DEVICE_TYPE_CPU, NULL, NULL, &status);
CHECK_CL_ERROR(status, "clCreateContextFromType", EXIT);
printf("get CPU context OK!\n");
// 2nd. computing device의 갯수를 얻는다.
// GPU라면 연산유닛갯수, CPU라면 코어갯수가 될것이다.
status = clGetContextInfo( context, CL_CONTEXT_DEVICES, 0, NULL, &num_devices);
CHECK_CL_ERROR(status, "clGetContextInfo", EXIT);
printf("device num : %d\n", num_devices);
if(num_devices == 0) goto EXIT;
// 3rd. device갯수만큼 디바이스를 핸들링할 id를 할당한다.
devices = (cl_device_id *) malloc(num_devices);
// 4th. 디바이스갯수만큼의 디바이스 id값을 얻어온다.
status = clGetContextInfo( context, CL_CONTEXT_DEVICES, num_devices, devices, NULL);
CHECK_CL_ERROR(status, "clGetContextInfo", EXIT);
// 5th. command큐를 만든다. 디바이스 단위로 커맨드큐는 만들수 있다.
// 하나 이상의 device가 있다면 실험으로 0번째 디바이스에 큐를 만든다.
cmd_queue = clCreateCommandQueue( context, devices[0], 0, &status);
CHECK_CL_ERROR(status, "clCreateCommandQueue", EXIT);
// 6th. kernel에 입력으로 줄 버퍼를 만든다. 호스트에서 정적으로 할당하고 할당된 포인터를 사용한다.
inputBuffer = clCreateBuffer( context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,
sizeof(cl_int) * 2, &input, &status);
CHECK_CL_ERROR(status, "clCreateBuffer", EXIT);
// 7th. kernel의 출력으로 받을 버퍼를 만든다. 호스트의 포인터를 사용한다.
outputBuffer = clCreateBuffer( context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
sizeof(cl_int) * 2, &output, &status);
CHECK_CL_ERROR(status, "clCreateBuffer", EXIT);
// 여기까지 정리..
// key1. 실제의 동작은 커맨드큐단위로 이루어진다.
// key2. 커맨드큐는 디바이스단위로 이루어진다.
// key3. kernel함수의 입출력력은 cl함수를 이용하여 버퍼로 할당한다.
// 8th. 커널의 소스이다. 별도의 소스파일로 작성해도 된다. 물론 소스없이 미리 빌드해서 바이너리로 만들어도 된다.
const char *program_source =
"__kernel void test(__global int * output, "
" __global int * input) "
"{"
//" *output = *input;"
" output[0] = input[1];"
" output[1] = input[0];"
"}";
// 9th. 만든 커널을 프로그램으로 설정한다.
printf("create a OpenCL program using the kernel source\n");
program = clCreateProgramWithSource( context, 1, &program_source, NULL, &status);
CHECK_CL_ERROR(status, "clCreateProgramWithSource", EXIT);
// 10th. 프로그램을 컴파일한다.
printf("building program\n");
status = clBuildProgram(program, 1, devices, NULL, NULL, NULL);
CHECK_CL_ERROR(status, "clCreateProgramWithSource", EXIT);
// 11th. 프로그램에서 test라는 함수를 커널로 생성한다.
printf("create kernel\n");
kernel = clCreateKernel(program, "test", &status);
CHECK_CL_ERROR(status, "clCreateKernel", EXIT);
// 12th. ND range를 설정한다. work-group은 하나고 work-item도 하나이다. 싱글프로세싱동작이다.
size_t global_work_size = 1;
size_t local_work_size = 1;
int argIdx = 0;
// 13th. 만들어진 커널(test함수)에 파라미터를 넣는다. 파라미터는 미리할당해둔 __global int *의 버퍼들이다.
printf("set kernel arguments\n");
// arg1
status = clSetKernelArg( kernel, argIdx, sizeof(cl_mem), (void *)&outputBuffer);
CHECK_CL_ERROR(status, "clSetKernelArg", EXIT);
argIdx ++;
// arg2
status = clSetKernelArg( kernel, argIdx, sizeof(cl_mem), (void *)&inputBuffer);
CHECK_CL_ERROR(status, "clSetKernelArg", EXIT);
argIdx ++;
// 14th. NDRange로 설정된 맵에 현재 디바이스의 큐에, 커널(test함수)를 집어 넣는다. 실행이 될것이다.
printf("{ executing kernels... (enqueue kernels)\n");
status = clEnqueueNDRangeKernel( cmd_queue, kernel, 1, NULL,
&global_work_size, &local_work_size,
0, NULL, NULL);
CHECK_CL_ERROR(status, "clEnqueueNDRangeKernel", EXIT);
printf("} executing kernels... (enqueue kernels)\n");
// 15th. 버퍼를 읽는함수는 자체로 barrier다. 결과가 끝나기를 기다려서 결과를 얻어온다.
status = clEnqueueReadBuffer( cmd_queue, outputBuffer, CL_TRUE, 0,
sizeof(cl_int), &output,
0, NULL, NULL);
CHECK_CL_ERROR(status, "clEnqueueReadBuffer", EXIT);
EXIT:
// 16th. 리소스를 반환한다.
if (inputBuffer != (cl_mem) 0)
clReleaseMemObject(inputBuffer);
if (outputBuffer != (cl_mem) 0)
clReleaseMemObject(outputBuffer);
if (kernel != (cl_kernel) 0)
clReleaseKernel(kernel);
if (program != (cl_program) 0)
clReleaseProgram(program);
if (cmd_queue != (cl_command_queue) 0)
clReleaseCommandQueue(cmd_queue);
if (context != (cl_context) 0)
clReleaseContext(context);
if (devices )
free(devices);
// 17th. 결과 출력
int i;
printf("\n input : \n \t");
for (i=0; i<2; i++) printf("%d ", input[i]);
printf("\n output : \n \t");
for (i=0; i<2; i++) printf("%d ", output[i]);
printf("\n");
return 0;
}
출처 : http://cafe.naver.com/opencl/12