OpenCL 교육 정리
이 문서는 2014/02/24, 25 양일간 3D융합기술지원센터에서 실시한 OpenCL 교육을 받고 내용을 정리한 문서입니다.
OpenCL 이란?
l OpenCL(Open Computing Language)은 개방형 범용 병렬 컴퓨팅 프레임워크이다.
l CPU, GPU, DSP 등의 프로세서로 이루어진 이종 플랫폼에서 실행되는 프로그램을 작성할 수 있게 해 준다.
l OpenCL은 커널 코드를 작성하기 위한 C99 기반의 언어인 OpenCL C와 플랫폼을 정의하고 제어하기 위한 API를 포함하고
있다.
l OpenCL은 작업 기반(task-based) 및 데이터 기반(data-based) 병렬 컴퓨팅을 제공한다.
l 최신 버전 : 2.0
l OpenCL platform model
l Execution Model
n kernel : device에서 실행되는 하나의 함수
n work-item : 가장 작은 단위의 실행 객체. work-item 단위로
나뉘어 각각 병렬처리.
n work-group : work-item 그룹.
n Global : 전체 work 영역
OpenCL 개발 환경 구축
Windows OS 에서 NVIDIA가 제공하는 CUDA Toolkit을 사용하여 개발하는 환경을 구축하는 것을 기준으로 설명함.
CUDA는 GPU에서 수행하는 병렬처리 알고리즘을 C 프로그래밍 언어 등의 언어를 사용하여 작성할 수 있도록 하는 GPGPU 기술로 NVIDIA의 지포스 8시리즈급 이상에서 지원함.
1.
Microsoft Visual Studio 준비
2.
NVIDIA CUDA 5.5 다운로드 및 설치
B.
반듯이 OS 버전 및 BIT(32/64) 에 맞는 설치 파일을 설치해야 함.
3.
참고
A.
OpenCL.dll 빌드 오류
i.
64비트 윈도우의 경우 디폴트로 로딩되는 OpenCL.dll 이 32비트 용으로 로딩되어 빌드가 정상적으로
되지 않는 경우 발생.
iii.
위 링크로 다운로드한 파일을 설치하여 intel OpenCL
x64 SDK 가 설치되도록 함.
OpenCL 프로젝트 설정
가장 기본적인 Win32 콘솔 응용 프로젝트를 생성하여 OpenCL 프로그래밍이 가능하도록
설정하는 방법을 설명함.
1.
Win32 콘솔 응용 프로그램 생성
2.
속성 > C/C++
A.
'일반'의
추가 포함 디렉터리 항목에 아래 경로 추가
i.
CUDA Toolkit 설치 경로/include
3.
속성 > 링커
A.
'일반'의
추가 라이브러리 디렉터리 항목에 아래 경로 추가
i.
CUDA Toolkit 설치 경로/lib/Win32
ii.
64bit OS 라고 x64 폴더를 지정해도 빌드가 제대로 되지 않음.
B.
'입력'의
추가 종속성 항목에 OpenCL.lib 추가.
OpenCL 프로그래밍
기본적으로 병렬 덧셈 프로그램을 작성해 본다.
1.
소스 파일에 sample.cl 파일 추가 후
아래 코드 추가
__kernel void vecAdd(__global int *a, __global int
*b, __global int *c) {
int id
= get_global_id(0);
c[id] =
a[id] + b[id];
}
|
2.
main cpp 파일에 아래 코드 추가
#include "stdafx.h"
#include
#include
#include
#define MEM_SIZE 128
#define MAX_SOURCE_SIZE 0x100000
int _tmain(int argc, _TCHAR* argv[])
{
// 변수 선언
cl_platform_id platform_id;
cl_uint ret_num_platforms;
cl_device_id device_id;
cl_uint ret_num_devices;
cl_context context;
cl_command_queue command_queue;
cl_mem memA;
cl_mem memB;
cl_mem memC;
char *source_str;
size_t source_size;
FILE *fp;
char filename[]="./sample.cl";
cl_program program;
cl_kernel kernel;
cl_int ret;
cl_int err;
size_t size;
int a[MEM_SIZE];
int b[MEM_SIZE];
int c[MEM_SIZE];
// Platform ID 얻기
ret =
clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
// Divice ID 얻기
ret =
clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, &device_id,
&ret_num_devices);
// Context 생성. context는 Device 리소스를
관리함.
context =
clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);
// Command Queue 생성. Command Queue를 통해
Device에 작업 시킴.
command_queue =
clCreateCommandQueue(context, device_id, 0, &ret);
// 계산할 변수에 값 할당
for (int i = 0; i
< MEM_SIZE; i++) {
a[i]
= i;
b[i]
= 4 * i;
}
// 계산에 사용되는 각 변수 메모리 할당
memA = clCreateBuffer(context,
CL_MEM_READ_WRITE, MEM_SIZE*sizeof(int), NULL, &ret);
memB =
clCreateBuffer(context, CL_MEM_READ_WRITE, MEM_SIZE*sizeof(int), NULL,
&ret);
memC =
clCreateBuffer(context, CL_MEM_READ_WRITE, MEM_SIZE*sizeof(int), NULL,
&ret);
// kernel에 입력해야하는 변수를 command queue에 할당
ret =
clEnqueueWriteBuffer(command_queue, memA, CL_TRUE, 0, MEM_SIZE*sizeof(int),
a, 0, NULL, NULL);
ret =
clEnqueueWriteBuffer(command_queue, memB, CL_TRUE, 0, MEM_SIZE*sizeof(int),
b, 0, NULL, NULL);
// kernel 함수 파일 읽어오기
fp =
fopen(filename, "r");
if (!fp) {
printf("Not
Kernel File. \n");
exit(1);
}
source_str =
(char*)malloc(MAX_SOURCE_SIZE * sizeof(char)) ;
source_size =
fread(source_str, 1, MAX_SOURCE_SIZE, fp);
fclose(fp);
// kernel 함수 string 으로 부터 program
생성
program =
clCreateProgramWithSource(context, 1, (const char**)&source_str, (const
size_t*)&source_size, &ret);
// program 빌드
ret =
clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
// kernel 생성. 두번째 param에 kernel 함수명을 정확기 기입하여야함.
kernel =
clCreateKernel(program, "sample", &ret);
// kernel 함수 param 지정
ret =
clSetKernelArg(kernel, 0, sizeof(cl_mem), (void*)&memA);
ret =
clSetKernelArg(kernel, 1, sizeof(cl_mem), (void*)&memB);
ret =
clSetKernelArg(kernel, 2, sizeof(cl_mem), (void*)&memC);
// global item 수 설정. 128번의 연산을 수행할 것이므로 128 설정.
size_t
global_item_size = 128;
// work_group 수 설정. global item 수에따른
work_group 분류로 global item 수에 나누어 떨어지는 수가 되어야함.
size_t
local_item_size = 1;
// kernel을 queue 할당. 실제 계산
수행.
ret = clEnqueueNDRangeKernel(command_queue,
kernel, 1, NULL, &global_item_size, &local_item_size, 0, NULL, NULL);
// 결과값 읽어오기
ret =
clEnqueueReadBuffer(command_queue, memC, CL_TRUE, 0, MEM_SIZE*sizeof(int), c,
0, NULL, NULL);
// 결과값 출력
for (int i = 0; i
< MEM_SIZE; i++) {
if
(i%4 == 0 ) printf("\n");
printf("%d\t",
c[i]);
}
// 메모리 해제
ret =
clFlush(command_queue);
ret =
clFinish(command_queue);
ret =
clReleaseKernel(kernel);
ret =
clReleaseProgram(program);
ret =
clReleaseMemObject(memA);
ret =
clReleaseMemObject(memB);
ret =
clReleaseMemObject(memC);
ret =
clReleaseCommandQueue(command_queue);
ret =
clReleaseContext(context);
return 0;
}
|
3.
빌드
4.
디버깅하지 않고 시작
5.
결과 확인
128번의 덧셈 계산 병렬 계산.
참고
l OpenCL 홈페이지
l OpenCL 2.0 Spec
l NVIDIA 예제 파일 다운로드
l 추천 도서
n OpenCL Programing Guide - AAFTAB MUNSHI 저 /
Addison Wesley
n OpenCL 프로그래밍 - 주식회사
Fixstars 저 / 한빛미디어
댓글 없음:
댓글 쓰기