我已将OpenCL编程作为大学项目的一部分,当我尝试在clCreateBuffer()例程期间将数据输入缓冲区对象时,我遇到了一些问题。
该程序是一个简单的二维矩阵加法。代码如下:
#define _CRT_SECURE_NO_WARNINGS
#define PROGRAM_FILE "add_kernel.cl"
#define ADD_FUNC "add_matrix"
#define MATRIX_DIM 256
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef MAC
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif
/* Find a GPU associated with the first available platform */
cl_device_id create_device() {
cl_platform_id platform;
cl_device_id dev;
int err;
/* Identify a platform */
err = clGetPlatformIDs(1, &platform, NULL);
if(err < 0) {
perror("Couldn't identify a platform");
exit(1);
}
/* Access a GPU */
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &dev, NULL);
if(err < 0) {
perror("Couldn't access any GPU type");
exit(1);
}
return dev;
}
cl_program build_program(cl_context ctx, cl_device_id dev, const char* filename) {
cl_program program;
FILE *program_handle;
char *program_buffer, *program_log;
size_t program_size, log_size;
int err;
/* Read program file and place content into buffer */
program_handle = fopen(filename, "r");
if(program_handle == NULL) {
perror("Couldn't find the program file");
exit(1);
}
fseek(program_handle, 0, SEEK_END);
program_size = ftell(program_handle);
rewind(program_handle);
program_buffer = (char*)malloc(program_size + 1);
program_buffer[program_size] = '\0';
fread(program_buffer, sizeof(char), program_size, program_handle);
fclose(program_handle);
/* Create program from file */
program = clCreateProgramWithSource(ctx, 1,
(const char**)&program_buffer, &program_size, &err);
if(err < 0) {
perror("Couldn't create the program");
exit(1);
}
free(program_buffer);
/* Build program */
err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
if(err < 0) {
/* Find size of log and print to std output */
clGetProgramBuildInfo(program, dev, CL_PROGRAM_BUILD_LOG,
0, NULL, &log_size);
program_log = (char*) malloc(log_size + 1);
program_log[log_size] = '\0';
clGetProgramBuildInfo(program, dev, CL_PROGRAM_BUILD_LOG,
log_size + 1, program_log, NULL);
printf("%s\n", program_log);
free(program_log);
exit(1);
}
return program;
}
int main(){
/* Host/device data structures */
cl_device_id device;
cl_context context;
cl_command_queue queue;
cl_program program;
cl_kernel add_kernel;
size_t global_size;
cl_ulong mem_size;
cl_int i, j, err, check;
/* Data and buffers */
cl_uint matrix_dim;
float a_mat[MATRIX_DIM][MATRIX_DIM], b_mat[MATRIX_DIM][MATRIX_DIM],
c_mat[MATRIX_DIM][MATRIX_DIM], check_mat[MATRIX_DIM][MATRIX_DIM];
cl_mem a_buffer, b_buffer, c_buffer;
/* Initialize A, B, and check matrices */
srand((unsigned int)time(0));
for(i=0; i<MATRIX_DIM; i++) {
for(j=0; j<MATRIX_DIM; j++) {
a_mat[i][j] = (float)rand()/RAND_MAX;
}
}
srand((unsigned int)time(0));
for(i=0; i<MATRIX_DIM; i++) {
for(j=0; j<MATRIX_DIM; j++) {
b_mat[i][j] = (float)rand()/RAND_MAX;
check_mat[i][j] = 0.0f;
}
}
for(i=0; i<MATRIX_DIM; i++) {
for(j=0; j<MATRIX_DIM; j++) {
check_mat[i][j] += a_mat[i][j] + b_mat[i][j];
}
}
/* Create a device and context */
device = create_device();
context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);
if(err < 0) {
perror("Couldn't create a context");
exit(1);
}
/* Build the program */
program = build_program(context, device, PROGRAM_FILE);
add_kernel = clCreateKernel(program, ADD_FUNC, &err);
if(err < 0) {
perror("Couldn't create a kernel");
exit(1);
};
/* Create buffers */
a_buffer = clCreateBuffer(context,
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(a_mat), a_mat, &err);
if(err < 0) {
perror("Couldn't create buffer A");
exit(1);
};
b_buffer = clCreateBuffer(context,
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(b_mat), b_mat, &err);
if(err < 0) {
perror("Couldn't create buffer B");
exit(1);
};
c_buffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
sizeof(c_mat), NULL, &err);
if(err < 0) {
perror("Couldn't create buffer C");
exit(1);
};
/* Create a command queue */
queue = clCreateCommandQueue(context, device, 0, &err);
if(err < 0) {
perror("Couldn't create a command queue");
exit(1);
};
/* Create arguments for multiplication kernel */
err = clSetKernelArg(add_kernel, 0, sizeof(a_buffer), &a_buffer);
err |= clSetKernelArg(add_kernel, 1, sizeof(b_buffer), &b_buffer);
err |= clSetKernelArg(add_kernel, 2, sizeof(c_buffer), &c_buffer);
global_size = MATRIX_DIM * MATRIX_DIM;
//printf("%lu\n", global_size);
err = clEnqueueNDRangeKernel(queue, add_kernel, 1, NULL, &global_size,
NULL, 0, NULL, NULL);
if(err < 0) {
perror("Couldn't enqueue the addition kernel");
exit(1);
}
/* Read output buffer */
err = clEnqueueReadBuffer(queue, c_buffer, CL_TRUE, 0,
sizeof(c_mat), c_mat, 0, NULL, NULL);
if(err < 0) {
perror("Couldn't read the buffer");
exit(1);
}
/* Check result */
check = 1;
for(i=0; i<MATRIX_DIM; i++) {
for(j=0; j<MATRIX_DIM; j++) {
if(c_mat[i][j] != check_mat[i][j]){
check = 0;
break;
}
}
}
if(check)
printf("Addition check succeeded.\n");
else
printf("Addition check failed.\n");
/* Deallocate resources */
clReleaseMemObject(a_buffer);
clReleaseMemObject(b_buffer);
clReleaseMemObject(c_buffer);
clReleaseKernel(add_kernel);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseContext(context);
return 0;
}
内核代码如下:
__kernel void add_matrix(__global float* matrix_a,
__global float* matrix_b,
__global float* result) {
int i = get_global_id(0);
result[i] = matrix_a[i] + matrix_b[i];
}
现在,它适用于高达358x358的尺寸,但是只要我在MATRIX_DIM中放入359就会崩溃。它显示通常的“foo.exe已停止工作”。我知道它必须使用clCreateBuffer()命令执行某些操作,因为如果我从第一个clCreateBuffer()及其下面删除代码,它会运行并终止正常,但只要我添加一个崩溃就会崩溃。
CL_DEVICE_MAX_MEM_ALLOC_SIZE选项显示512MB的可用内存,而我尝试传递的数据远远少于此数据。
我有什么办法可以增加我可以处理的数据量吗?
我的GPU是Radeon Sapphire HD5770
编辑:在评论中提出建议后,我运行了调试器,产生了以下消息:
Program received signal SIGSEGV, Segmentation fault.
In amdocl!_aclHsaLoader () (C:\WINDOWS\SysWOW64\amdocl.dll)
#15 0x00401355 in create_device () at C:\test\testcl.c:26
C:\test\testcl.c:26:503:beg:0x401355
我真的不确定这意味着什么。有什么想法吗?
答案 0 :(得分:0)
主要问题是,你在这些代码行上分配了很多内存,所以,你得到了一个堆栈溢出:
float a_mat[MATRIX_DIM][MATRIX_DIM], b_mat[MATRIX_DIM][MATRIX_DIM],
c_mat[MATRIX_DIM][MATRIX_DIM], check_mat[MATRIX_DIM][MATRIX_DIM];
在我的测试中,执行甚至没有进入main
方法。您必须使用以下内容在堆上分配这些矩阵:
float *a_mat = calloc(MATRIX_DIM*MATRIX_DIM, sizeof(*a_mat));
float *b_mat = calloc(MATRIX_DIM*MATRIX_DIM, sizeof(*b_mat));
float *c_mat = calloc(MATRIX_DIM*MATRIX_DIM, sizeof(*c_mat));
float *check_mat = calloc(MATRIX_DIM*MATRIX_DIM, sizeof(*check_mat));
但是现在,每个矩阵只有一维(1D)数据缓冲区,因此,您必须将每个 2D索引[i][j]
更改为相应的1D索引[i*MATRIX_DIM][j]
,例如:
a_mat[i*MATRIX_DIM+j] = (float)rand()/RAND_MAX;
编辑:您还必须更新对clCreateBuffer
和clEnqueueReadBuffer
的来电。无法再使用sizeof(matrix_name)
确定矩阵大小(其中matrix_name
是a_mat
,b_mat
之一,...)。您必须使用sizeof
替换每个MATRIX_DIM*MATRIX_DIM*sizeof(*matrix_name)
(有4个)。不要忘记matrix_name
之前的derefence,例如:
a_buffer = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
MATRIX_DIM*MATRIX_DIM*sizeof(*a_mat), a_mat, &err);
(编辑结束)。
不要忘记最后释放数据缓冲区:
free(a_mat);
free(b_mat);
free(c_mat);
free(check_mat);
为了让内核运行,我甚至还要修复内核程序的读取。 ftell
的返回值总是有点过大。实际的字节数由fread
返回。因此,改变这些行
program_buffer[program_size] = '\0';
fread(program_buffer, sizeof(char), program_size, program_handle);
到
program_size = fread(program_buffer, sizeof(char), program_size, program_handle); // changed
program_buffer[program_size] = '\0'; // moved here