Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在OpenCL中使用ClenqueueEndRange内核_Opencl - Fatal编程技术网

在OpenCL中使用ClenqueueEndRange内核

在OpenCL中使用ClenqueueEndRange内核,opencl,Opencl,我需要OpenCL中一个函数的帮助。当我开始使用ClenqueueEndRangeKernel而不是clEnqueueTask时,程序需要更多的时间才能成功。为什么会这样?据我所知,该程序应该使用数据并行模型,它会工作得更快,我错了吗?如果是,我如何更改代码以查看数据并行模型的实际工作 __kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut, unsigned

我需要OpenCL中一个函数的帮助。当我开始使用ClenqueueEndRangeKernel而不是clEnqueueTask时,程序需要更多的时间才能成功。为什么会这样?据我所知,该程序应该使用数据并行模型,它会工作得更快,我错了吗?如果是,我如何更改代码以查看数据并行模型的实际工作

__kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut, unsigned int InSize, unsigned int OutSize)
{
    for (int i = 0, j = 0; i < InSize; i+=4, j++)
    {
        unsigned char Value = (pDataIn[i] + pDataIn[i + 1] + pDataIn[i + 2]) / 3;
        pDataOut[j] = Value;
    }
}

int iWidth, iHeight, iBpp;
vector<unsigned char> pDataIn;
vector<unsigned char> pDataOut;


int err = LoadBmpFile(L"3840x2160.bmp", iWidth, iHeight, iBpp, pDataIn);

if (err != 0 || pDataIn.size() == 0 || iBpp != 32)
{
    std::cout << "error load input file!\n";
}

pDataOut.resize(pDataIn.size()/4);


cl_device_id device_id = NULL;
cl_context context = NULL;
cl_command_queue command_queue = NULL;
cl_mem memobj = NULL;
cl_mem memobj1 = NULL;
cl_program program = NULL;
cl_kernel kernel = NULL;
cl_platform_id platform_id = NULL;
cl_uint ret_num_devices;
cl_uint ret_num_platforms;
cl_int ret;

unsigned int SizeIn, SizeOut;

SizeIn = pDataIn.size();
SizeOut = pDataOut.size();

FILE *fp;
char fileName[] = "./kernel.cl";
char *source_str;
size_t source_size;

//Loading kernel
fp = fopen(fileName, "r");
if (!fp) {
    fprintf(stderr, "Failed to load kernel.\n");
    system("PAUSE");
    exit(1);
}
source_str = (char*)malloc(MAX_SOURCE_SIZE);
source_size = fread(source_str, 1, MAX_SOURCE_SIZE, fp);
fclose(fp);

//Getting Platform and Device
ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);

ret = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &ret_num_devices);


//Create context
context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);


//create kernel program
program = clCreateProgramWithSource(context, 1, (const char **)&source_str,
(const size_t *)&source_size, &ret);

//build it
ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);

//create queue
command_queue = clCreateCommandQueue(context, device_id, 0, &ret);

//create bufer
memobj = clCreateBuffer(context, CL_MEM_READ_WRITE, pDataIn.size(), NULL, &ret);

memobj1 = clCreateBuffer(context, CL_MEM_READ_WRITE,pDataOut.size(), NULL, &ret);
//copy buffer to kernel

ret = clEnqueueWriteBuffer(command_queue, memobj, CL_TRUE, 0, pDataIn.size(), pDataIn.data(), 0, NULL, NULL);


//create opencl kernel
kernel = clCreateKernel(program, "red_to_green", &ret);


//set kernel args
ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&memobj);
ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&memobj1);
ret = clSetKernelArg(kernel, 2, sizeof(unsigned int), (void *)&SizeIn);
ret = clSetKernelArg(kernel, 3, sizeof(unsigned int), (void *)&SizeOut);

const size_t cycles_max = 10;
clock_t t0 = clock();
for (int i = 0; i<cycles_max; i++){

    float start_time =  clock();
    float search_time = 0;
    //float last_time = 0;

    //execute opencl kernel
    //ret = clEnqueueTask(command_queue, kernel, 0, NULL, NULL);

    size_t global_item_size = 8;
    size_t local_item_size = 4;

    ret = clEnqueueNDRangeKernel(command_queue,kernel, 1, NULL, &global_item_size, &local_item_size, 0, NULL, NULL);

    //copy from buffer
    ret = clEnqueueReadBuffer(command_queue, memobj1, CL_TRUE, 0, pDataOut.size(), pDataOut.data(), 0, NULL, NULL);

    ret = clFinish(command_queue);

    float end_time = clock();
    search_time = end_time - start_time;
    //float last_time = last_time + search_time;
    cout << search_time << endl;

}

clock_t t1 = clock();
double time_seconds = (t1-t0)*CLOCKS_PER_SEC/cycles_max;
cout << time_seconds/1000 <<endl;
WriteBmpFile(L"3840x2160_wb.bmp", iWidth, iHeight, 8, pDataOut.size(), pDataOut.data(), false);
system("PAUSE");
内核使用单个工作项执行

clEnqueueTask相当于使用 工时尺寸=1,全局工时偏移=NULL,全局工时尺寸[0]设置为1, 本地工作大小[0]设置为1

当您使用ClenqueueEndRangeKernel时,您使用的是由4个工作项组成的2个工作组,但它们都在做相同的工作。它们都从相同的全局内存中读取,但更重要的是,它们都试图写入全局内存中的相同位置

在进行计算时,需要考虑工人的全局id

__kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut, unsigned int InSize, unsigned int OutSize)
{
    int gid = get_global_id(0);
    int gsize = get_global_size(0);

    for (int j = gid; j < (InSize >> 2); j+= gsize)
    {
        unsigned char Value = (pDataIn[j*4] + pDataIn[j*4 + 1] + pDataIn[j*4 + 2]) / 3;
        pDataOut[j] = Value;
    }
}
内核使用单个工作项执行

clEnqueueTask相当于使用 工时尺寸=1,全局工时偏移=NULL,全局工时尺寸[0]设置为1, 本地工作大小[0]设置为1

当您使用ClenqueueEndRangeKernel时,您使用的是由4个工作项组成的2个工作组,但它们都在做相同的工作。它们都从相同的全局内存中读取,但更重要的是,它们都试图写入全局内存中的相同位置

在进行计算时,需要考虑工人的全局id

__kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut, unsigned int InSize, unsigned int OutSize)
{
    int gid = get_global_id(0);
    int gsize = get_global_size(0);

    for (int j = gid; j < (InSize >> 2); j+= gsize)
    {
        unsigned char Value = (pDataIn[j*4] + pDataIn[j*4 + 1] + pDataIn[j*4 + 2]) / 3;
        pDataOut[j] = Value;
    }
}

看起来您正在迭代内核中输入图像的所有像素。这将导致所有线程计算所有像素的图像强度。尝试为每个像素启动一个线程。为此,请将内核源代码更改为仅计算一个像素的输出值:

__kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut) {
    int j = get_global_id(0);
    int i = j*4;
    pDataOut[i] = (pDataIn[j] + pDataIn[j + 1] + pDataIn[j + 2]) / 3;
}
此代码现在将对位置i处的单个像素的RGBA输入图像的RGB值执行平均。现在,您需要做的就是启动尽可能多的线程,因为您的图像有像素。相关变化:

//create opencl kernel
kernel = clCreateKernel(program, "black_white_img", &ret);


//set kernel args
ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&memobj);
ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&memobj1);

const size_t cycles_max = 10;
clock_t t0 = clock();
for (int i = 0; i<cycles_max; i++){

float start_time =  clock();
float search_time = 0;
//float last_time = 0;

//execute opencl kernel
//ret = clEnqueueTask(command_queue, kernel, 0, NULL, NULL);

size_t global_item_size = iWidth * iHeight;

ret = clEnqueueNDRangeKernel(command_queue,kernel, 1, NULL, &global_item_size, NULL, 0, NULL, NULL);

与您的代码相比,这将提供相当大的加速

看起来您正在迭代内核中输入图像的所有像素。这将导致所有线程计算所有像素的图像强度。尝试为每个像素启动一个线程。为此,请将内核源代码更改为仅计算一个像素的输出值:

__kernel void black_white_img(__global unsigned char *pDataIn, __global unsigned char *pDataOut) {
    int j = get_global_id(0);
    int i = j*4;
    pDataOut[i] = (pDataIn[j] + pDataIn[j + 1] + pDataIn[j + 2]) / 3;
}
此代码现在将对位置i处的单个像素的RGBA输入图像的RGB值执行平均。现在,您需要做的就是启动尽可能多的线程,因为您的图像有像素。相关变化:

//create opencl kernel
kernel = clCreateKernel(program, "black_white_img", &ret);


//set kernel args
ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&memobj);
ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&memobj1);

const size_t cycles_max = 10;
clock_t t0 = clock();
for (int i = 0; i<cycles_max; i++){

float start_time =  clock();
float search_time = 0;
//float last_time = 0;

//execute opencl kernel
//ret = clEnqueueTask(command_queue, kernel, 0, NULL, NULL);

size_t global_item_size = iWidth * iHeight;

ret = clEnqueueNDRangeKernel(command_queue,kernel, 1, NULL, &global_item_size, NULL, 0, NULL, NULL);

与您的代码相比,这将提供相当大的加速

对于克伦奎茨克,我使用了一个工作组和一个工作项,对吗?说清楚,是的。谢谢你的回答!但它仍然比使用克伦奎茨克慢50毫秒,而不是20-30毫秒。还好吧,还是我做错了什么?您还可以解释一下全局项目大小和局部项目大小是如何工作的,以及为什么我们在这个函数中需要它们,而在clEnqueueTask中不需要它们?请尝试一个工作组,即:全局项目大小=局部项目大小=64。另外,确保InSize是大的-我会选择至少10k,但100多万会更好。在有大量工作要做之前,并行性不会很明显。尝试过,但仍然工作得比较慢。也许你可以给我一些链接,让我可以更深入地研究这个主题?无论如何,非常感谢!对于克伦奎茨克,我使用了一个工作组和一个工作项,对吗?说清楚,是的。谢谢你的回答!但它仍然比使用克伦奎茨克慢50毫秒,而不是20-30毫秒。还好吧,还是我做错了什么?您还可以解释一下全局项目大小和局部项目大小是如何工作的,以及为什么我们在这个函数中需要它们,而在clEnqueueTask中不需要它们?请尝试一个工作组,即:全局项目大小=局部项目大小=64。另外,确保InSize是大的-我会选择至少10k,但100多万会更好。在有大量工作要做之前,并行性不会很明显。尝试过,但仍然工作得比较慢。也许你可以给我一些链接,让我可以更深入地研究这个主题?无论如何,非常感谢!